From bf3f351d91a899b891c0c81a437f6841f2c4d099 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sat, 29 Nov 2025 14:55:44 -0500 Subject: [PATCH 01/36] Set `format_special=FALSE` by default --- R/qd_dag.R | 7 ++++--- man/qd_dag.Rd | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/R/qd_dag.R b/R/qd_dag.R index 9a61e56..7ff0735 100644 --- a/R/qd_dag.R +++ b/R/qd_dag.R @@ -11,7 +11,7 @@ #' @param edge_aes_opts A list feeding aesthetic options for edges to #' [DiagrammeR::edge_aes()]. Defaults to empty list. #' @param format_special Render numeric elements in an alphanumeric `alpha_id` as -#' subcripts. Defaults to `TRUE`. +#' subcripts. Defaults to `FALSE`. #' @param verbose Indicate whether to print node and edge dataframes to the console. #' See Details below. Defaults to `TRUE`. #' @param check_dag Logical. Check whether the graph conforms to the rules of DAGs. @@ -51,8 +51,9 @@ #' qd_dag <- function(edgelist, node_labs = NULL, node_aes_opts = list(), edge_aes_opts = list(), - format_special = TRUE, - verbose = FALSE, check_dag = TRUE, theme = "base", ...) { + format_special = FALSE, + verbose = FALSE, check_dag = TRUE, + theme = "base", ...) { # Identify Nodes -------------------------------------------------------- ## extract unique nodes, sort in ascending order diff --git a/man/qd_dag.Rd b/man/qd_dag.Rd index f930617..d2b9260 100644 --- a/man/qd_dag.Rd +++ b/man/qd_dag.Rd @@ -9,7 +9,7 @@ qd_dag( node_labs = NULL, node_aes_opts = list(), edge_aes_opts = list(), - format_special = TRUE, + format_special = FALSE, verbose = FALSE, check_dag = TRUE, theme = "base", @@ -29,7 +29,7 @@ to \code{NULL}.} \code{\link[DiagrammeR:edge_aes]{DiagrammeR::edge_aes()}}. Defaults to empty list.} \item{format_special}{Render numeric elements in an alphanumeric \code{alpha_id} as -subcripts. Defaults to \code{TRUE}.} +subcripts. Defaults to \code{FALSE}.} \item{verbose}{Indicate whether to print node and edge dataframes to the console. See Details below. Defaults to \code{TRUE}.} From 8eefd024fec5b470abd76fb7c03418489ef437d8 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 30 Nov 2025 12:11:14 -0500 Subject: [PATCH 02/36] Cosmetic --- R/qd_todagitty.R | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/R/qd_todagitty.R b/R/qd_todagitty.R index ffa3d76..1ecf966 100644 --- a/R/qd_todagitty.R +++ b/R/qd_todagitty.R @@ -29,10 +29,10 @@ qd_todagitty <- function(edgelist, diagram_type = "dag", showplot = FALSE, exposure, outcome, ...) { - dagitty_obj <- dagitty::dagitty(paste(diagram_type, "{", - paste(edgelist, collapse = "; "), - "}"), - layout = TRUE) + dagitty_obj <- dagitty::dagitty( + paste(diagram_type, "{", paste(edgelist, collapse = "; "), "}"), + layout = TRUE + ) ## optional to show dagitty plot if (showplot) { @@ -40,7 +40,9 @@ qd_todagitty <- function(edgelist, diagram_type = "dag", showplot = FALSE, } ## use dagitty's algorithm to identify adjustment sets - sets <- dagitty::adjustmentSets(dagitty_obj, exposure = exposure, - outcome = outcome, ...) + sets <- dagitty::adjustmentSets(dagitty_obj, + exposure = exposure, + outcome = outcome, + ...) return(sets) } From f4cb16949ab92907440d857221f69e00c63a0ffa Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 30 Nov 2025 12:39:26 -0500 Subject: [PATCH 03/36] Update function name and documentation --- NAMESPACE | 1 + R/qd_todagitty.R | 38 ++++++++++++++++-------- man/qd_adjustment_sets.Rd | 62 +++++++++++++++++++++++++++++++++++++++ man/qd_todagitty.Rd | 46 ----------------------------- 4 files changed, 88 insertions(+), 59 deletions(-) create mode 100644 man/qd_adjustment_sets.Rd delete mode 100644 man/qd_todagitty.Rd diff --git a/NAMESPACE b/NAMESPACE index ba3fbd3..83e5731 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -4,6 +4,7 @@ export(get_conditioned_nodes) export(parse_edges) export(parse_edgestring) export(parse_nodes) +export(qd_adjustment_sets) export(qd_dag) export(qd_save) export(qd_swig) diff --git a/R/qd_todagitty.R b/R/qd_todagitty.R index 1ecf966..69e4b05 100644 --- a/R/qd_todagitty.R +++ b/R/qd_todagitty.R @@ -3,31 +3,38 @@ #' @description #' Format an edgelist and send it to dagitty to identify variable adjustment sets. #' -#' @param edgelist A vector of edge relationships. Must be strictly organized (see -#' example for format). +#' @param edgelist A vector of edge relationships. #' @param diagram_type Character identifying the diagram type. Defaults to "dag", but -#' user can specify another graph type (see dagitty documentation). +#' user can specify another graph type (see [dagitty::adjustmentSets()]). #' @param showplot Logical indicating whether to produce a dagitty plot. Defaults to #' `FALSE`. #' @param exposure Character. Specify exposure of interest. (Required) #' @param outcome Character. Specifiy outcome of interest. (Required) -#' @param ... Pass arguments to [dagitty::adjustmentSets()]. See dagitty documentation -#' for options. +#' @param ... Pass arguments to [dagitty::adjustmentSets()]. #' #' @details -#' The `exposure` and `outcome` options map to dagitty functions of the same name. +#' The `exposure` and `outcome` options map to dagitty parameters of the same name. #' +#' [qd_todagitty()] remains an alias for [qd_adjustment_sets()] to avoid breaking +#' existing scripts, as it was the original name of this function. +#' +#' @rdname qd_adjustment_sets #' @export #' @examples +#' # feed an edgelist to qd_adjustment_sets() #' edges <- c("A -> { B C D }", #' "B -> C", -#' "E -> { B C }") -#' # must pass exposure and outcome arguments to dagitty::adjustmentSets() -#' qd_todagitty(edges, exposure = "A", outcome = "C") -#' qd_todagitty(edges, exposure = "A", outcome = "C", type = "minimal") -qd_todagitty <- function(edgelist, diagram_type = "dag", showplot = FALSE, - exposure, outcome, - ...) { +#' "E -> { B C }", +#' "Y <- L -> A") +#' qd_adjustment_sets(edges, exposure = "A", outcome = "C") +#' qd_adjustment_sets(edges, exposure = "A", outcome = "C", type = "minimal") +#' +#' # if you've already created a qd_dag() object +#' dag <- qd_dag(edges) +#' qd_adjustment_sets(dag$qd_edgelist) +qd_adjustment_sets <- function(edgelist, diagram_type = "dag", showplot = FALSE, + exposure, outcome, + ...) { dagitty_obj <- dagitty::dagitty( paste(diagram_type, "{", paste(edgelist, collapse = "; "), "}"), @@ -46,3 +53,8 @@ qd_todagitty <- function(edgelist, diagram_type = "dag", showplot = FALSE, ...) return(sets) } + + +#' @rdname qd_adjustment_sets +#' @export +qd_todagitty <- qd_adjustment_sets diff --git a/man/qd_adjustment_sets.Rd b/man/qd_adjustment_sets.Rd new file mode 100644 index 0000000..0aa96ae --- /dev/null +++ b/man/qd_adjustment_sets.Rd @@ -0,0 +1,62 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/qd_todagitty.R +\name{qd_adjustment_sets} +\alias{qd_adjustment_sets} +\alias{qd_todagitty} +\title{Identify variables for adjustment} +\usage{ +qd_adjustment_sets( + edgelist, + diagram_type = "dag", + showplot = FALSE, + exposure, + outcome, + ... +) + +qd_todagitty( + edgelist, + diagram_type = "dag", + showplot = FALSE, + exposure, + outcome, + ... +) +} +\arguments{ +\item{edgelist}{A vector of edge relationships.} + +\item{diagram_type}{Character identifying the diagram type. Defaults to "dag", but +user can specify another graph type (see \code{\link[dagitty:adjustmentSets]{dagitty::adjustmentSets()}}).} + +\item{showplot}{Logical indicating whether to produce a dagitty plot. Defaults to +\code{FALSE}.} + +\item{exposure}{Character. Specify exposure of interest. (Required)} + +\item{outcome}{Character. Specifiy outcome of interest. (Required)} + +\item{...}{Pass arguments to \code{\link[dagitty:adjustmentSets]{dagitty::adjustmentSets()}}.} +} +\description{ +Format an edgelist and send it to dagitty to identify variable adjustment sets. +} +\details{ +The \code{exposure} and \code{outcome} options map to dagitty parameters of the same name. + +\code{\link[=qd_todagitty]{qd_todagitty()}} remains an alias for \code{\link[=qd_adjustment_sets]{qd_adjustment_sets()}} to avoid breaking +existing scripts, as it was the original name of this function. +} +\examples{ +# feed an edgelist to qd_adjustment_sets() +edges <- c("A -> { B C D }", + "B -> C", + "E -> { B C }", + "Y <- L -> A") +qd_adjustment_sets(edges, exposure = "A", outcome = "C") +qd_adjustment_sets(edges, exposure = "A", outcome = "C", type = "minimal") + +# if you've already created a qd_dag() object +dag <- qd_dag(edges) +qd_adjustment_sets(dag$qd_edgelist) +} diff --git a/man/qd_todagitty.Rd b/man/qd_todagitty.Rd deleted file mode 100644 index 50350fd..0000000 --- a/man/qd_todagitty.Rd +++ /dev/null @@ -1,46 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/qd_todagitty.R -\name{qd_todagitty} -\alias{qd_todagitty} -\title{Identify variables for adjustment} -\usage{ -qd_todagitty( - edgelist, - diagram_type = "dag", - showplot = FALSE, - exposure, - outcome, - ... -) -} -\arguments{ -\item{edgelist}{A vector of edge relationships. Must be strictly organized (see -example for format).} - -\item{diagram_type}{Character identifying the diagram type. Defaults to "dag", but -user can specify another graph type (see dagitty documentation).} - -\item{showplot}{Logical indicating whether to produce a dagitty plot. Defaults to -\code{FALSE}.} - -\item{exposure}{Character. Specify exposure of interest. (Required)} - -\item{outcome}{Character. Specifiy outcome of interest. (Required)} - -\item{...}{Pass arguments to \code{\link[dagitty:adjustmentSets]{dagitty::adjustmentSets()}}. See dagitty documentation -for options.} -} -\description{ -Format an edgelist and send it to dagitty to identify variable adjustment sets. -} -\details{ -The \code{exposure} and \code{outcome} options map to dagitty functions of the same name. -} -\examples{ -edges <- c("A -> { B C D }", - "B -> C", - "E -> { B C }") -# must pass exposure and outcome arguments to dagitty::adjustmentSets() -qd_todagitty(edges, exposure = "A", outcome = "C") -qd_todagitty(edges, exposure = "A", outcome = "C", type = "minimal") -} From df24eb69f1ea38c1b5c83d7430180506cb24fef0 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 30 Nov 2025 13:05:39 -0500 Subject: [PATCH 04/36] Add more informative theme error --- R/themes.R | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/R/themes.R b/R/themes.R index 3cf5683..5e02d81 100644 --- a/R/themes.R +++ b/R/themes.R @@ -24,6 +24,10 @@ qd_themes <- function(graph_obj, theme, ...) { "pearl" = "theme_qd_pearl" ) + if (!theme %in% names(select_theme)) { + stop("`theme` must be one of: ", paste(names(select_theme), collapse = ", ")) + } + do.call(select_theme[theme], args = list(graph_obj = graph_obj, ...)) } From 1444d3e60446e7969f7f6008abec265c3b7a83e9 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 30 Nov 2025 13:05:53 -0500 Subject: [PATCH 05/36] Set conditioned shape to "rectangle" --- R/themes.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/themes.R b/R/themes.R index 5e02d81..7a3291a 100644 --- a/R/themes.R +++ b/R/themes.R @@ -5,7 +5,7 @@ #' #' @param graph_obj A DAG object created by [qd_dag()]. #' @param conditioned A character vector indicating which nodes are conditioned upon. -#' The shape for these nodes will be set to "square". +#' The shape for these nodes will be set to "rectangle". #' @param theme A character string indicating the theme to use. Defaults to "base". #' Set to `NULL` to use GraphViz defaults. #' @param font A character vector indicating the font family to use for node labels. @@ -117,7 +117,7 @@ get_conditioned_nodes <- function(graph_obj, conditioned = NULL) { DiagrammeR::set_node_attrs("height", default_minht) |> # select conditioned nodes and update node aesthetics DiagrammeR::select_nodes_by_id(cd_nodes) |> - DiagrammeR::set_node_attrs_ws("shape", "square") |> + DiagrammeR::set_node_attrs_ws("shape", "rectangle") |> DiagrammeR::set_node_attrs_ws("width", "0") |> DiagrammeR::set_node_attrs_ws("height", "0") |> DiagrammeR::clear_selection() From def5744bc1b2e61b35eda05c36b2691cb2d7bf9b Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 30 Nov 2025 21:53:38 -0500 Subject: [PATCH 06/36] Fix logic in choosing returned object --- R/sep_opts.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/sep_opts.R b/R/sep_opts.R index ce3c980..64bb151 100644 --- a/R/sep_opts.R +++ b/R/sep_opts.R @@ -40,11 +40,11 @@ sep_opts <- function(entity = NULL, table = FALSE) { ) } - if (entity %in% names(defopts)) { + if (is.null(entity)) { + defopts + } else if (entity %in% names(defopts)) { defopts[names(defopts) == entity] - } else if (!is.null(entity)) { - entity } else { - defopts + entity } } From 66b6c5871a73d2de3d59a8bbe0025c712de87d10 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 30 Nov 2025 23:14:23 -0500 Subject: [PATCH 07/36] Add global options --- R/qd_dag.R | 8 +++++--- R/qd_save.R | 3 ++- R/qd_swig.R | 2 +- R/utils.R | 12 ++++++++++++ man/qd_dag.Rd | 8 ++++---- man/qd_save.Rd | 2 +- man/qd_swig.Rd | 2 +- man/qd_themes.Rd | 2 +- 8 files changed, 27 insertions(+), 12 deletions(-) diff --git a/R/qd_dag.R b/R/qd_dag.R index 7ff0735..e42e6e9 100644 --- a/R/qd_dag.R +++ b/R/qd_dag.R @@ -51,9 +51,11 @@ #' qd_dag <- function(edgelist, node_labs = NULL, node_aes_opts = list(), edge_aes_opts = list(), - format_special = FALSE, - verbose = FALSE, check_dag = TRUE, - theme = "base", ...) { + format_special = getOption("quickdag.format_special"), + verbose = getOption("quickdag.verbose"), + check_dag = getOption("quickdag.check_dag"), + theme = getOption("quickdag.theme"), + ...) { # Identify Nodes -------------------------------------------------------- ## extract unique nodes, sort in ascending order diff --git a/R/qd_save.R b/R/qd_save.R index 6618f29..c8beb7c 100644 --- a/R/qd_save.R +++ b/R/qd_save.R @@ -33,7 +33,8 @@ #' #' # clean up temporary directory #' file.remove(file) -qd_save <- function(graph, file_name, ..., embed = FALSE, kg = NULL) { +qd_save <- function(graph, file_name, ..., + embed = getOption("quickdag.embed"), kg = NULL) { DiagrammeR::export_graph(graph = graph, file_name = file_name, ...) if (embed == TRUE) { diff --git a/R/qd_swig.R b/R/qd_swig.R index ecd8b46..dd38569 100644 --- a/R/qd_swig.R +++ b/R/qd_swig.R @@ -32,7 +32,7 @@ qd_swig <- function(graph_obj, fixed_nodes, custom_values = NULL, - fixed_sep = "vlin", + fixed_sep = getOption("quickdag.fixed_sep"), sep_point_size = 15) { ndf <- DiagrammeR::get_node_df(graph_obj) diff --git a/R/utils.R b/R/utils.R index 88b12a1..db770dc 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,2 +1,14 @@ # Put some variables in the global environment to avoid R CMD CHECK notes utils::globalVariables(c("alpha_id", "label", "start", "end")) + +.onLoad <- function(libname, pkgname) { + opts <- list( + quickdag.check_dag = TRUE, + quickdag.verbose = FALSE, + quickdag.format_special = FALSE, + quickdag.fixed_sep = "vlin", + quickdag.theme = "base", + quickdag.embed = FALSE + ) + options(opts) +} diff --git a/man/qd_dag.Rd b/man/qd_dag.Rd index d2b9260..b8e3d01 100644 --- a/man/qd_dag.Rd +++ b/man/qd_dag.Rd @@ -9,10 +9,10 @@ qd_dag( node_labs = NULL, node_aes_opts = list(), edge_aes_opts = list(), - format_special = FALSE, - verbose = FALSE, - check_dag = TRUE, - theme = "base", + format_special = getOption("quickdag.format_special"), + verbose = getOption("quickdag.verbose"), + check_dag = getOption("quickdag.check_dag"), + theme = getOption("quickdag.theme"), ... ) } diff --git a/man/qd_save.Rd b/man/qd_save.Rd index 0285083..33b69cd 100644 --- a/man/qd_save.Rd +++ b/man/qd_save.Rd @@ -4,7 +4,7 @@ \alias{qd_save} \title{Save graph objects as images} \usage{ -qd_save(graph, file_name, ..., embed = FALSE, kg = NULL) +qd_save(graph, file_name, ..., embed = getOption("quickdag.embed"), kg = NULL) } \arguments{ \item{graph}{A graph object produced by \code{\link[=qd_dag]{qd_dag()}}} diff --git a/man/qd_swig.Rd b/man/qd_swig.Rd index 9fb49b2..72272da 100644 --- a/man/qd_swig.Rd +++ b/man/qd_swig.Rd @@ -8,7 +8,7 @@ qd_swig( graph_obj, fixed_nodes, custom_values = NULL, - fixed_sep = "vlin", + fixed_sep = getOption("quickdag.fixed_sep"), sep_point_size = 15 ) } diff --git a/man/qd_themes.Rd b/man/qd_themes.Rd index e06afe0..4db01a6 100644 --- a/man/qd_themes.Rd +++ b/man/qd_themes.Rd @@ -31,7 +31,7 @@ Set to \code{NULL} to use GraphViz defaults.} Defaults to "serif".} \item{conditioned}{A character vector indicating which nodes are conditioned upon. -The shape for these nodes will be set to "square".} +The shape for these nodes will be set to "rectangle".} } \description{ Apply various pre-fabricated themes to diagrams. From c9d3bb47ee2e93e9b9a57236b5ada60da1885069 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Mon, 1 Dec 2025 00:11:22 -0500 Subject: [PATCH 08/36] Add basic node and edge modification via alpha_ids Partially addresses #10 --- NAMESPACE | 6 ++++ R/graph_operations.R | 67 +++++++++++++++++++++++++++++++++++++++++ man/graph_operations.Rd | 47 +++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 R/graph_operations.R create mode 100644 man/graph_operations.Rd diff --git a/NAMESPACE b/NAMESPACE index 83e5731..9129b32 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -7,9 +7,15 @@ export(parse_nodes) export(qd_adjustment_sets) export(qd_dag) export(qd_save) +export(qd_select_edges) +export(qd_select_nodes) +export(qd_set_edge_attrs) +export(qd_set_node_attrs) export(qd_swig) export(qd_themes) export(qd_todagitty) +export(select_edges_by_node_alpha_id) +export(select_nodes_by_alpha_id) export(sep_opts) export(theme_qd_base) export(theme_qd_circles) diff --git a/R/graph_operations.R b/R/graph_operations.R new file mode 100644 index 0000000..8b731ac --- /dev/null +++ b/R/graph_operations.R @@ -0,0 +1,67 @@ +#' Graph operations +#' +#' @param graph_obj A `quickdag` object output by [qd_dag()] or [qd_swig()]. +#' @param alpha_ids A vector of alphanumeric node IDs upon which to operate. +#' @param ... Pass other arguments to `DiagrammeR` functions: +#' [DiagrammeR::set_node_attrs()], [DiagrammeR::set_edge_attrs()]. +#' @param from_alpha A vector of alphanumeric source node IDs. +#' @param to_alpha A vector of alphanumeric destination node IDs. +#' @param set_op Passed to the `set_op` argument of [DiagrammeR::select_nodes_by_id()] +#' or [DiagrammeR::select_edges_by_node_id()]. Defaults to "union". +#' +#' @rdname graph_operations +#' @export +qd_set_node_attrs <- function(graph_obj, alpha_ids, ..., set_op = "union") { + numids <- get_numids(graph_obj, alpha_ids) + graph_out <- DiagrammeR::set_node_attrs(graph_obj, ..., nodes = numids) +} + +#' @rdname graph_operations +#' @export +qd_set_edge_attrs <- function(graph_obj, ..., from_alpha = NULL, to_alpha = NULL, + set_op = "union") { + from_numids <- NULL + to_numids <- NULL + if (!is.null(from_alpha)) { + from_numids <- get_numids(graph_obj, from_alpha) + } + if (!is.null(to_alpha)) { + to_numids <- get_numids(graph_obj, to_alpha) + } + graph_out <- DiagrammeR::set_edge_attrs(graph_obj, ..., + from = from_numids, to = to_numids) + graph_out +} + +#' @rdname graph_operations +#' @export +select_nodes_by_alpha_id <- function(graph_obj, alpha_ids, set_op = "union") { + numids <- get_numids(graph_obj, alpha_ids) + graph_out <- DiagrammeR::select_nodes_by_id(graph_obj, + nodes = numids, + set_op = set_op) + graph_out +} + +#' @rdname graph_operations +#' @export +qd_select_nodes <- select_nodes_by_alpha_id + +#' @rdname graph_operations +#' @export +select_edges_by_node_alpha_id <- function(graph_obj, alpha_ids, set_op = "union") { + numids <- get_numids(graph_obj, alpha_ids) + graph_out <- DiagrammeR::select_edges_by_node_id(graph_obj, + nodes = numids, + set_op = set_op) +} + +#' @rdname graph_operations +#' @export +qd_select_edges <- select_edges_by_node_alpha_id + +get_numids <- function(graph_obj, alpha_ids) { + graph_obj$nodes_df |> + dplyr::filter(alpha_id %in% alpha_ids) |> + dplyr::pull(id) +} diff --git a/man/graph_operations.Rd b/man/graph_operations.Rd new file mode 100644 index 0000000..f858085 --- /dev/null +++ b/man/graph_operations.Rd @@ -0,0 +1,47 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/graph_operations.R +\name{qd_set_node_attrs} +\alias{qd_set_node_attrs} +\alias{qd_set_edge_attrs} +\alias{select_nodes_by_alpha_id} +\alias{qd_select_nodes} +\alias{select_edges_by_node_alpha_id} +\alias{qd_select_edges} +\title{Graph operations} +\usage{ +qd_set_node_attrs(graph_obj, alpha_ids, ..., set_op = "union") + +qd_set_edge_attrs( + graph_obj, + ..., + from_alpha = NULL, + to_alpha = NULL, + set_op = "union" +) + +select_nodes_by_alpha_id(graph_obj, alpha_ids, set_op = "union") + +qd_select_nodes(graph_obj, alpha_ids, set_op = "union") + +select_edges_by_node_alpha_id(graph_obj, alpha_ids, set_op = "union") + +qd_select_edges(graph_obj, alpha_ids, set_op = "union") +} +\arguments{ +\item{graph_obj}{A \code{quickdag} object output by \code{\link[=qd_dag]{qd_dag()}} or \code{\link[=qd_swig]{qd_swig()}}.} + +\item{alpha_ids}{A vector of alphanumeric node IDs upon which to operate.} + +\item{...}{Pass other arguments to \code{DiagrammeR} functions: +\code{\link[DiagrammeR:set_node_attrs]{DiagrammeR::set_node_attrs()}}, \code{\link[DiagrammeR:set_edge_attrs]{DiagrammeR::set_edge_attrs()}}.} + +\item{set_op}{Passed to the \code{set_op} argument of \code{\link[DiagrammeR:select_nodes_by_id]{DiagrammeR::select_nodes_by_id()}} +or \code{\link[DiagrammeR:select_edges_by_node_id]{DiagrammeR::select_edges_by_node_id()}}. Defaults to "union".} + +\item{from_alpha}{A vector of alphanumeric source node IDs.} + +\item{to_alpha}{A vector of alphanumeric destination node IDs.} +} +\description{ +Graph operations +} From 79722b4a8e6a8240ece9d93fb7973aa60ef83b88 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Tue, 2 Dec 2025 11:31:17 -0500 Subject: [PATCH 09/36] Cosmetic --- R/utils.R | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/R/utils.R b/R/utils.R index db770dc..dded936 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,14 +1,15 @@ # Put some variables in the global environment to avoid R CMD CHECK notes utils::globalVariables(c("alpha_id", "label", "start", "end")) +# Set global options .onLoad <- function(libname, pkgname) { opts <- list( quickdag.check_dag = TRUE, - quickdag.verbose = FALSE, - quickdag.format_special = FALSE, + quickdag.embed = FALSE, quickdag.fixed_sep = "vlin", + quickdag.format_special = FALSE, quickdag.theme = "base", - quickdag.embed = FALSE + quickdag.verbose = FALSE ) options(opts) } From 6e2929dd3c216a905e8da866465e64df4774802e Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Tue, 2 Dec 2025 11:52:25 -0500 Subject: [PATCH 10/36] Add documentation for get_numds() --- R/graph_operations.R | 2 ++ man/get_numids.Rd | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 man/get_numids.Rd diff --git a/R/graph_operations.R b/R/graph_operations.R index 8b731ac..9763564 100644 --- a/R/graph_operations.R +++ b/R/graph_operations.R @@ -60,6 +60,8 @@ select_edges_by_node_alpha_id <- function(graph_obj, alpha_ids, set_op = "union" #' @export qd_select_edges <- select_edges_by_node_alpha_id +#' Retrieves numeric IDs given alphanumeric IDs +#' @inheritParams qd_set_node_attrs get_numids <- function(graph_obj, alpha_ids) { graph_obj$nodes_df |> dplyr::filter(alpha_id %in% alpha_ids) |> diff --git a/man/get_numids.Rd b/man/get_numids.Rd new file mode 100644 index 0000000..6c77f18 --- /dev/null +++ b/man/get_numids.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/graph_operations.R +\name{get_numids} +\alias{get_numids} +\title{Retrieves numeric IDs given alphanumeric IDs} +\usage{ +get_numids(graph_obj, alpha_ids) +} +\arguments{ +\item{graph_obj}{A \code{quickdag} object output by \code{\link[=qd_dag]{qd_dag()}} or \code{\link[=qd_swig]{qd_swig()}}.} + +\item{alpha_ids}{A vector of alphanumeric node IDs upon which to operate.} +} +\description{ +Retrieves numeric IDs given alphanumeric IDs +} From 440f3fac5100fd282632bb3ca5f0d06eadf1bdd9 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Tue, 2 Dec 2025 11:52:25 -0500 Subject: [PATCH 11/36] Add documentation for get_numids() --- R/graph_operations.R | 2 ++ man/get_numids.Rd | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 man/get_numids.Rd diff --git a/R/graph_operations.R b/R/graph_operations.R index 8b731ac..9763564 100644 --- a/R/graph_operations.R +++ b/R/graph_operations.R @@ -60,6 +60,8 @@ select_edges_by_node_alpha_id <- function(graph_obj, alpha_ids, set_op = "union" #' @export qd_select_edges <- select_edges_by_node_alpha_id +#' Retrieves numeric IDs given alphanumeric IDs +#' @inheritParams qd_set_node_attrs get_numids <- function(graph_obj, alpha_ids) { graph_obj$nodes_df |> dplyr::filter(alpha_id %in% alpha_ids) |> diff --git a/man/get_numids.Rd b/man/get_numids.Rd new file mode 100644 index 0000000..6c77f18 --- /dev/null +++ b/man/get_numids.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/graph_operations.R +\name{get_numids} +\alias{get_numids} +\title{Retrieves numeric IDs given alphanumeric IDs} +\usage{ +get_numids(graph_obj, alpha_ids) +} +\arguments{ +\item{graph_obj}{A \code{quickdag} object output by \code{\link[=qd_dag]{qd_dag()}} or \code{\link[=qd_swig]{qd_swig()}}.} + +\item{alpha_ids}{A vector of alphanumeric node IDs upon which to operate.} +} +\description{ +Retrieves numeric IDs given alphanumeric IDs +} From c0c68f349a9d22d70e33270b20beb26e7ce5414e Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 28 Dec 2025 21:42:49 -0600 Subject: [PATCH 12/36] Tweak comment --- R/qd_dag.R | 3 +-- man/qd_dag.Rd | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/R/qd_dag.R b/R/qd_dag.R index e42e6e9..caa67ca 100644 --- a/R/qd_dag.R +++ b/R/qd_dag.R @@ -31,7 +31,7 @@ #' edges <- c("A -> { B C } <- L", #' "B -> C") #' -#' # make a DAG object and render the graph using the default theme +#' # Make a DAG object and render the graph using the default theme #' g.obj <- qd_dag(edges) #' DiagrammeR::render_graph(g.obj) #' @@ -174,5 +174,4 @@ qd_dag <- function(edgelist, node_labs = NULL, class(graph) <- c("quickdag", "dgr_graph") graph - } diff --git a/man/qd_dag.Rd b/man/qd_dag.Rd index b8e3d01..56c6f88 100644 --- a/man/qd_dag.Rd +++ b/man/qd_dag.Rd @@ -56,7 +56,7 @@ the same letter as their corresponding \code{alpha_id}, which may not always be edges <- c("A -> { B C } <- L", "B -> C") -# make a DAG object and render the graph using the default theme +# Make a DAG object and render the graph using the default theme g.obj <- qd_dag(edges) DiagrammeR::render_graph(g.obj) From 68861146c2e9bd6906aee5c6118ac557a6e18ed2 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 28 Dec 2025 21:43:46 -0600 Subject: [PATCH 13/36] Store theme and conditioned info to DAG object --- R/themes.R | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/R/themes.R b/R/themes.R index 7a3291a..05f8044 100644 --- a/R/themes.R +++ b/R/themes.R @@ -28,6 +28,12 @@ qd_themes <- function(graph_obj, theme, ...) { stop("`theme` must be one of: ", paste(names(select_theme), collapse = ", ")) } + graph_obj$theme <- theme + + if ("conditioned" %in% names(match.call())) { + graph_obj$conditioned <- match.call()$conditioned + } + do.call(select_theme[theme], args = list(graph_obj = graph_obj, ...)) } From 5f1f9ad9695ed0d5640ce61b7c4fced46eb8b0b6 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 28 Dec 2025 21:44:19 -0600 Subject: [PATCH 14/36] Get basic "pearl" theme working --- R/themes.R | 35 +++++++++++++++++++++++++++-------- man/qd_themes.Rd | 8 +++++++- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/R/themes.R b/R/themes.R index 05f8044..c5f138c 100644 --- a/R/themes.R +++ b/R/themes.R @@ -83,22 +83,41 @@ theme_qd_circles <- function(graph_obj, font = "serif", ...) { #' @rdname qd_themes #' @export -theme_qd_pearl <- function(graph_obj, font = "serif", ...) { +theme_qd_pearl <- function(graph_obj, font = "serif", + pointsize = 0.02, + linewidths = 0.2, + ...) { # set base theme graph_obj <- graph_obj |> theme_qd_base() # tweak base theme graph_obj <- graph_obj |> # node attribute tweaks - DiagrammeR::add_global_graph_attrs("shape", "point", "node") |> - DiagrammeR::add_global_graph_attrs("width", 0.2, "node") |> - DiagrammeR::add_global_graph_attrs("height", 0.2, "node") |> + DiagrammeR::add_global_graph_attrs("shape", "point", "node") |> + DiagrammeR::add_global_graph_attrs("style", "filled", "node") |> + DiagrammeR::add_global_graph_attrs("fixedsize", TRUE, "node") |> + DiagrammeR::add_global_graph_attrs("width", pointsize, "node") |> + DiagrammeR::add_global_graph_attrs("height", pointsize, "node") |> + DiagrammeR::add_global_graph_attrs("fontsize", 5, "node") |> # edge attribute tweaks - DiagrammeR::add_global_graph_attrs("penwidth", 0.2, "edge") |> - DiagrammeR::add_global_graph_attrs("arrowsize", 0.2, "edge") + DiagrammeR::add_global_graph_attrs("penwidth", linewidths, "edge") |> + DiagrammeR::add_global_graph_attrs("arrowsize", linewidths, "edge") |> + DiagrammeR::add_global_graph_attrs("arrowhead", "vee", "edge") - if (exists("conditioned")) { - messaging::emit_message("This theme does not allow for conditioned nodes.") + ## Necessary because fillcolor via global attributes appears to be broken + ## in DiagrammeR + graph_obj$nodes_df$fillcolor <- "black" + + ## Add and style external labels + graph_obj$nodes_df$xlabel <- graph_obj$nodes_df$label + graph_obj$nodes_df$fontcolor <- "black" + + if ("conditioned" %in% names(match.call())) { + cdnodes <- match.call()$conditioned + graph_obj <- graph_obj |> get_conditioned_nodes(...) + graph_obj$nodes_df$label <- "" # Hide internal node labels + graph_obj$nodes_df$width[graph_obj$nodes_df$alpha_id %in% cdnodes] <- pointsize + graph_obj$nodes_df$height[graph_obj$nodes_df$alpha_id %in% cdnodes] <- pointsize } graph_obj diff --git a/man/qd_themes.Rd b/man/qd_themes.Rd index 4db01a6..d1c408e 100644 --- a/man/qd_themes.Rd +++ b/man/qd_themes.Rd @@ -14,7 +14,13 @@ theme_qd_base(graph_obj, font = "serif", ...) theme_qd_circles(graph_obj, font = "serif", ...) -theme_qd_pearl(graph_obj, font = "serif", ...) +theme_qd_pearl( + graph_obj, + font = "serif", + pointsize = 0.02, + linewidths = 0.2, + ... +) get_conditioned_nodes(graph_obj, conditioned = NULL) } From e919164ece191ff24864092c88925da1cbce3686 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 28 Dec 2025 23:08:15 -0600 Subject: [PATCH 15/36] Add cleanup_existing_themes() --- NAMESPACE | 1 + R/themes.R | 11 +++++++++++ man/qd_themes.Rd | 3 +++ 3 files changed, 15 insertions(+) diff --git a/NAMESPACE b/NAMESPACE index 9129b32..75baf4a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +export(cleanup_existing_theme) export(get_conditioned_nodes) export(parse_edges) export(parse_edgestring) diff --git a/R/themes.R b/R/themes.R index c5f138c..427a928 100644 --- a/R/themes.R +++ b/R/themes.R @@ -28,6 +28,7 @@ qd_themes <- function(graph_obj, theme, ...) { stop("`theme` must be one of: ", paste(names(select_theme), collapse = ", ")) } + graph_obj <- graph_obj |> cleanup_existing_theme() graph_obj$theme <- theme if ("conditioned" %in% names(match.call())) { @@ -42,6 +43,8 @@ qd_themes <- function(graph_obj, theme, ...) { #' @export theme_qd_base <- function(graph_obj, font = "serif", ...) { + graph_obj <- graph_obj |> cleanup_existing_theme() + graph_attrs <- tibble::tibble( attr = c("rankdir", "layout"), value = c("LR", "dot"), @@ -147,5 +150,13 @@ get_conditioned_nodes <- function(graph_obj, conditioned = NULL) { DiagrammeR::set_node_attrs_ws("height", "0") |> DiagrammeR::clear_selection() } + +#' @rdname qd_themes +#' @export +cleanup_existing_theme <- function(graph_obj) { + graph_obj$nodes_df <- graph_obj$nodes_df[, c("id", "type", "label", "alpha_id")] + ## Used for the side effect of returning an empty global_attrs table in the + ## appropriate format + graph_obj$global_attrs <- qd_dag("A", theme = NULL, check_dag = FALSE)$global_attrs graph_obj } diff --git a/man/qd_themes.Rd b/man/qd_themes.Rd index d1c408e..aee79b0 100644 --- a/man/qd_themes.Rd +++ b/man/qd_themes.Rd @@ -6,6 +6,7 @@ \alias{theme_qd_circles} \alias{theme_qd_pearl} \alias{get_conditioned_nodes} +\alias{cleanup_existing_theme} \title{Diagram themes} \usage{ qd_themes(graph_obj, theme, ...) @@ -23,6 +24,8 @@ theme_qd_pearl( ) get_conditioned_nodes(graph_obj, conditioned = NULL) + +cleanup_existing_theme(graph_obj) } \arguments{ \item{graph_obj}{A DAG object created by \code{\link[=qd_dag]{qd_dag()}}.} From 268c137537ae1b1e8e010fb30f7315302ba509ac Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 28 Dec 2025 23:08:34 -0600 Subject: [PATCH 16/36] Save theme name; tweak 'pearl' --- R/themes.R | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/R/themes.R b/R/themes.R index 427a928..8fbb89b 100644 --- a/R/themes.R +++ b/R/themes.R @@ -67,12 +67,14 @@ theme_qd_base <- function(graph_obj, font = "serif", ...) { graph_obj <- graph_obj |> get_conditioned_nodes(...) + graph_obj$theme <- "base" graph_obj } #' @rdname qd_themes #' @export theme_qd_circles <- function(graph_obj, font = "serif", ...) { + # set base theme graph_obj <- graph_obj |> theme_qd_base() @@ -81,7 +83,7 @@ theme_qd_circles <- function(graph_obj, font = "serif", ...) { DiagrammeR::add_global_graph_attrs("shape", "circle", "node") graph_obj <- graph_obj |> get_conditioned_nodes(...) - graph_obj + graph_obj$theme <- "circles" } #' @rdname qd_themes @@ -116,13 +118,14 @@ theme_qd_pearl <- function(graph_obj, font = "serif", graph_obj$nodes_df$fontcolor <- "black" if ("conditioned" %in% names(match.call())) { - cdnodes <- match.call()$conditioned graph_obj <- graph_obj |> get_conditioned_nodes(...) + cd_nodes <- graph_obj$conditioned graph_obj$nodes_df$label <- "" # Hide internal node labels - graph_obj$nodes_df$width[graph_obj$nodes_df$alpha_id %in% cdnodes] <- pointsize - graph_obj$nodes_df$height[graph_obj$nodes_df$alpha_id %in% cdnodes] <- pointsize + graph_obj$nodes_df$width[graph_obj$nodes_df$alpha_id %in% cd_nodes] <- pointsize + graph_obj$nodes_df$height[graph_obj$nodes_df$alpha_id %in% cd_nodes] <- pointsize } + graph_obj$theme <- "pearl" graph_obj } @@ -150,6 +153,9 @@ get_conditioned_nodes <- function(graph_obj, conditioned = NULL) { DiagrammeR::set_node_attrs_ws("height", "0") |> DiagrammeR::clear_selection() } + graph_obj$conditioned <- conditioned + graph_obj +} #' @rdname qd_themes #' @export From 52efec321fee5cdc0bcf0884f5bc72cb74c710a6 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 28 Dec 2025 23:40:47 -0600 Subject: [PATCH 17/36] Add global options for 'pearl' theme --- R/themes.R | 19 +++++++++---------- R/utils.R | 6 +++++- man/qd_themes.Rd | 6 ++++-- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/R/themes.R b/R/themes.R index 8fbb89b..c099ab3 100644 --- a/R/themes.R +++ b/R/themes.R @@ -89,8 +89,10 @@ theme_qd_circles <- function(graph_obj, font = "serif", ...) { #' @rdname qd_themes #' @export theme_qd_pearl <- function(graph_obj, font = "serif", - pointsize = 0.02, - linewidths = 0.2, + pointsize = getOption("quickdag.pearl_pointsize"), + linewidths = getOption("quickdag.pearl_linewidths"), + fontsize = getOption("quickdag.pearl_fontsize"), + fontcolor = getOption("quickdag.pearl_fontcolor"), ...) { # set base theme graph_obj <- graph_obj |> theme_qd_base() @@ -100,29 +102,26 @@ theme_qd_pearl <- function(graph_obj, font = "serif", # node attribute tweaks DiagrammeR::add_global_graph_attrs("shape", "point", "node") |> DiagrammeR::add_global_graph_attrs("style", "filled", "node") |> - DiagrammeR::add_global_graph_attrs("fixedsize", TRUE, "node") |> DiagrammeR::add_global_graph_attrs("width", pointsize, "node") |> DiagrammeR::add_global_graph_attrs("height", pointsize, "node") |> - DiagrammeR::add_global_graph_attrs("fontsize", 5, "node") |> + DiagrammeR::add_global_graph_attrs("fixedsize", TRUE, "node") |> + DiagrammeR::add_global_graph_attrs("fontsize", fontsize, "node") |> # edge attribute tweaks DiagrammeR::add_global_graph_attrs("penwidth", linewidths, "edge") |> - DiagrammeR::add_global_graph_attrs("arrowsize", linewidths, "edge") |> - DiagrammeR::add_global_graph_attrs("arrowhead", "vee", "edge") + DiagrammeR::add_global_graph_attrs("arrowsize", linewidths, "edge") ## Necessary because fillcolor via global attributes appears to be broken ## in DiagrammeR graph_obj$nodes_df$fillcolor <- "black" ## Add and style external labels - graph_obj$nodes_df$xlabel <- graph_obj$nodes_df$label - graph_obj$nodes_df$fontcolor <- "black" + graph_obj$nodes_df$xlabel <- graph_obj$nodes_df$label + graph_obj$nodes_df$fontcolor <- fontcolor if ("conditioned" %in% names(match.call())) { graph_obj <- graph_obj |> get_conditioned_nodes(...) cd_nodes <- graph_obj$conditioned graph_obj$nodes_df$label <- "" # Hide internal node labels - graph_obj$nodes_df$width[graph_obj$nodes_df$alpha_id %in% cd_nodes] <- pointsize - graph_obj$nodes_df$height[graph_obj$nodes_df$alpha_id %in% cd_nodes] <- pointsize } graph_obj$theme <- "pearl" diff --git a/R/utils.R b/R/utils.R index dded936..e09e0fc 100644 --- a/R/utils.R +++ b/R/utils.R @@ -9,7 +9,11 @@ utils::globalVariables(c("alpha_id", "label", "start", "end")) quickdag.fixed_sep = "vlin", quickdag.format_special = FALSE, quickdag.theme = "base", - quickdag.verbose = FALSE + quickdag.verbose = FALSE, + quickdag.pearl_pointsize = 0.02, + quickdag.pearl_linewidths = 0.2, + quickdag.pearl_fontsize = 5, + quickdag.pearl_fontcolor = "black" ) options(opts) } diff --git a/man/qd_themes.Rd b/man/qd_themes.Rd index aee79b0..42d3505 100644 --- a/man/qd_themes.Rd +++ b/man/qd_themes.Rd @@ -18,8 +18,10 @@ theme_qd_circles(graph_obj, font = "serif", ...) theme_qd_pearl( graph_obj, font = "serif", - pointsize = 0.02, - linewidths = 0.2, + pointsize = getOption("quickdag.pearl_pointsize"), + linewidths = getOption("quickdag.pearl_linewidths"), + fontsize = getOption("quickdag.pearl_fontsize"), + fontcolor = getOption("quickdag.pearl_fontcolor"), ... ) From 7867d8456248bdce76e49f15fa18d70e9ec85a13 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 28 Dec 2025 23:54:42 -0600 Subject: [PATCH 18/36] Return graph_out --- R/graph_operations.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/graph_operations.R b/R/graph_operations.R index 9763564..1fa066e 100644 --- a/R/graph_operations.R +++ b/R/graph_operations.R @@ -54,6 +54,7 @@ select_edges_by_node_alpha_id <- function(graph_obj, alpha_ids, set_op = "union" graph_out <- DiagrammeR::select_edges_by_node_id(graph_obj, nodes = numids, set_op = set_op) + graph_out } #' @rdname graph_operations From 30322d4518ed55d7b2feac469cb9ed1656e772c4 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Mon, 29 Dec 2025 11:23:04 -0600 Subject: [PATCH 19/36] Use explicit argument names for ... These arguments get passed to DiagrammeR functions. Better to be explicit. --- R/graph_operations.R | 25 +++++++++++++++++-------- man/graph_operations.Rd | 22 +++++++++++++--------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/R/graph_operations.R b/R/graph_operations.R index 1fa066e..67c3a46 100644 --- a/R/graph_operations.R +++ b/R/graph_operations.R @@ -2,8 +2,10 @@ #' #' @param graph_obj A `quickdag` object output by [qd_dag()] or [qd_swig()]. #' @param alpha_ids A vector of alphanumeric node IDs upon which to operate. -#' @param ... Pass other arguments to `DiagrammeR` functions: -#' [DiagrammeR::set_node_attrs()], [DiagrammeR::set_edge_attrs()]. +#' @param node_attr Passed to [DiagrammeR::set_node_attrs()] argument of the same name. +#' @param edge_attr Passed to [DiagrammeR::set_edge_attrs()] argument of the same name. +#' @param values Passed to [DiagrammeR::set_node_attrs()] or +#' [DiagrammeR::set_edge_attrs()] argument of the same name. #' @param from_alpha A vector of alphanumeric source node IDs. #' @param to_alpha A vector of alphanumeric destination node IDs. #' @param set_op Passed to the `set_op` argument of [DiagrammeR::select_nodes_by_id()] @@ -11,15 +13,19 @@ #' #' @rdname graph_operations #' @export -qd_set_node_attrs <- function(graph_obj, alpha_ids, ..., set_op = "union") { +qd_set_node_attrs <- function(graph_obj, node_attr, values, alpha_ids) { numids <- get_numids(graph_obj, alpha_ids) - graph_out <- DiagrammeR::set_node_attrs(graph_obj, ..., nodes = numids) + graph_out <- DiagrammeR::set_node_attrs(graph_obj, + node_attr = node_attr, + values = values, + nodes = numids) + graph_out } #' @rdname graph_operations #' @export -qd_set_edge_attrs <- function(graph_obj, ..., from_alpha = NULL, to_alpha = NULL, - set_op = "union") { +qd_set_edge_attrs <- function(graph_obj, edge_attr, values, + from_alpha = NULL, to_alpha = NULL) { from_numids <- NULL to_numids <- NULL if (!is.null(from_alpha)) { @@ -28,8 +34,11 @@ qd_set_edge_attrs <- function(graph_obj, ..., from_alpha = NULL, to_alpha = NULL if (!is.null(to_alpha)) { to_numids <- get_numids(graph_obj, to_alpha) } - graph_out <- DiagrammeR::set_edge_attrs(graph_obj, ..., - from = from_numids, to = to_numids) + graph_out <- DiagrammeR::set_edge_attrs(graph_obj, + edge_attr = edge_attr, + values = values, + from = from_numids, + to = to_numids) graph_out } diff --git a/man/graph_operations.Rd b/man/graph_operations.Rd index f858085..0ed27a5 100644 --- a/man/graph_operations.Rd +++ b/man/graph_operations.Rd @@ -9,14 +9,14 @@ \alias{qd_select_edges} \title{Graph operations} \usage{ -qd_set_node_attrs(graph_obj, alpha_ids, ..., set_op = "union") +qd_set_node_attrs(graph_obj, node_attr, values, alpha_ids) qd_set_edge_attrs( graph_obj, - ..., + edge_attr, + values, from_alpha = NULL, - to_alpha = NULL, - set_op = "union" + to_alpha = NULL ) select_nodes_by_alpha_id(graph_obj, alpha_ids, set_op = "union") @@ -30,17 +30,21 @@ qd_select_edges(graph_obj, alpha_ids, set_op = "union") \arguments{ \item{graph_obj}{A \code{quickdag} object output by \code{\link[=qd_dag]{qd_dag()}} or \code{\link[=qd_swig]{qd_swig()}}.} -\item{alpha_ids}{A vector of alphanumeric node IDs upon which to operate.} +\item{node_attr}{Passed to \code{\link[DiagrammeR:set_node_attrs]{DiagrammeR::set_node_attrs()}} argument of the same name.} -\item{...}{Pass other arguments to \code{DiagrammeR} functions: -\code{\link[DiagrammeR:set_node_attrs]{DiagrammeR::set_node_attrs()}}, \code{\link[DiagrammeR:set_edge_attrs]{DiagrammeR::set_edge_attrs()}}.} +\item{values}{Passed to \code{\link[DiagrammeR:set_node_attrs]{DiagrammeR::set_node_attrs()}} or +\code{\link[DiagrammeR:set_edge_attrs]{DiagrammeR::set_edge_attrs()}} argument of the same name.} -\item{set_op}{Passed to the \code{set_op} argument of \code{\link[DiagrammeR:select_nodes_by_id]{DiagrammeR::select_nodes_by_id()}} -or \code{\link[DiagrammeR:select_edges_by_node_id]{DiagrammeR::select_edges_by_node_id()}}. Defaults to "union".} +\item{alpha_ids}{A vector of alphanumeric node IDs upon which to operate.} + +\item{edge_attr}{Passed to \code{\link[DiagrammeR:set_edge_attrs]{DiagrammeR::set_edge_attrs()}} argument of the same name.} \item{from_alpha}{A vector of alphanumeric source node IDs.} \item{to_alpha}{A vector of alphanumeric destination node IDs.} + +\item{set_op}{Passed to the \code{set_op} argument of \code{\link[DiagrammeR:select_nodes_by_id]{DiagrammeR::select_nodes_by_id()}} +or \code{\link[DiagrammeR:select_edges_by_node_id]{DiagrammeR::select_edges_by_node_id()}}. Defaults to "union".} } \description{ Graph operations From fe9374dd3b84989033e38435da114cb79afc79c1 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Mon, 29 Dec 2025 11:42:32 -0600 Subject: [PATCH 20/36] Cosmetic --- R/themes.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/themes.R b/R/themes.R index c099ab3..ecb2417 100644 --- a/R/themes.R +++ b/R/themes.R @@ -19,9 +19,9 @@ qd_themes <- function(graph_obj, theme, ...) { select_theme <- c( - "base" = "theme_qd_base", + "base" = "theme_qd_base", "circles" = "theme_qd_circles", - "pearl" = "theme_qd_pearl" + "pearl" = "theme_qd_pearl" ) if (!theme %in% names(select_theme)) { From 276d2472465aa14f19b7ce0f2af5885260f9906c Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Mon, 29 Dec 2025 12:56:28 -0600 Subject: [PATCH 21/36] New theme opts and cleanup conditioned processing --- R/themes.R | 78 +++++++++++++++++++++++++++++++----------------- R/utils.R | 11 +++++++ man/qd_themes.Rd | 56 ++++++++++++++++++++++++++-------- 3 files changed, 106 insertions(+), 39 deletions(-) diff --git a/R/themes.R b/R/themes.R index ecb2417..7f38f84 100644 --- a/R/themes.R +++ b/R/themes.R @@ -1,22 +1,33 @@ #' Diagram themes #' #' @description -#' Apply various pre-fabricated themes to diagrams. +#' Themes and theming utilies. #' #' @param graph_obj A DAG object created by [qd_dag()]. -#' @param conditioned A character vector indicating which nodes are conditioned upon. -#' The shape for these nodes will be set to "rectangle". #' @param theme A character string indicating the theme to use. Defaults to "base". #' Set to `NULL` to use GraphViz defaults. -#' @param font A character vector indicating the font family to use for node labels. -#' Defaults to "serif". -#' @param ... Pass arguments to theme call (e.g., [theme_qd_base()]), such as -#' `conditioned` or `font`. +#' @param conditioned A character vector indicating which nodes are conditioned upon. +#' The shape for these nodes will be set to "rectangle". +#' @param linewidths Size (in points) of edges and node outlines. By default, controlled +#' by package options specific to each theme, each of which defaults to 0.2. +#' @param fontname Font name for text elements of the graph. By default, controlled by +#' package options specific to each theme, each of which defaults to "Helvetica". This +#' is the default in `DiagrammeR`. +#' @param fontsize Size of text (in points). By default, controlled by package options +#' specific to each theme, each of which defaults to 5. +#' @param fontcolor Text color. By default, controlled by package options +#' specific to each theme, each of which defaults to "black". +#' @param pointsize Size of "point" shape (in points). Applies to "pearl" theme only and +#' defaults to 0.02. +#' @param pointcolor Border color for the "point" shape. Applies to "pearl" theme only +#' and defaults to "black". +#' @param pointfill Fill color for the "point" shape. Applies to "pearl" theme only +#' and defaults to "black". #' @rdname qd_themes #' @export # wrapper for theme selection -qd_themes <- function(graph_obj, theme, ...) { +qd_themes <- function(graph_obj, theme, conditioned = NULL) { select_theme <- c( "base" = "theme_qd_base", @@ -31,17 +42,22 @@ qd_themes <- function(graph_obj, theme, ...) { graph_obj <- graph_obj |> cleanup_existing_theme() graph_obj$theme <- theme - if ("conditioned" %in% names(match.call())) { - graph_obj$conditioned <- match.call()$conditioned + if (!is.null(conditioned)) { + graph_obj$conditioned <- conditioned } do.call(select_theme[theme], - args = list(graph_obj = graph_obj, ...)) + args = list(graph_obj = graph_obj, conditioned = conditioned)) } #' @rdname qd_themes #' @export -theme_qd_base <- function(graph_obj, font = "serif", ...) { +theme_qd_base <- function(graph_obj, + linewidths = getOption("quickdag.base_linewidths"), + fontname = getOption("quickdag.base_fontname"), + fontsize = getOption("quickdag.base_fontsize"), + fontcolor = getOption("quickdag.base_fontcolor"), + conditioned = NULL) { graph_obj <- graph_obj |> cleanup_existing_theme() @@ -53,7 +69,7 @@ theme_qd_base <- function(graph_obj, font = "serif", ...) { node_attrs <- tibble::tibble( attr = c("shape", "penwidth", "fontname", "width", "height"), - value = c("plaintext", "0.5", font, "0", "0"), + value = c("plaintext", "0.5", fontname, "0", "0"), attr_type = "node" ) @@ -65,15 +81,20 @@ theme_qd_base <- function(graph_obj, font = "serif", ...) { graph_obj$global_attrs <- dplyr::bind_rows(graph_attrs, node_attrs, edge_attrs) + graph_obj <- graph_obj |> get_conditioned_nodes(conditioned = conditioned) - graph_obj <- graph_obj |> get_conditioned_nodes(...) graph_obj$theme <- "base" graph_obj } #' @rdname qd_themes #' @export -theme_qd_circles <- function(graph_obj, font = "serif", ...) { +theme_qd_circles <- function(graph_obj, + linewidths = getOption("quickdag.circles_linewidths"), + fontname = getOption("quickdag.circles_fontname"), + fontsize = getOption("quickdag.circles_fontsize"), + fontcolor = getOption("quickdag.circles_fontcolor"), + conditioned = NULL) { # set base theme graph_obj <- graph_obj |> theme_qd_base() @@ -82,18 +103,23 @@ theme_qd_circles <- function(graph_obj, font = "serif", ...) { graph_obj <- graph_obj |> DiagrammeR::add_global_graph_attrs("shape", "circle", "node") - graph_obj <- graph_obj |> get_conditioned_nodes(...) + graph_obj <- graph_obj |> get_conditioned_nodes(conditioned = conditioned) + graph_obj$theme <- "circles" + graph_obj } #' @rdname qd_themes #' @export -theme_qd_pearl <- function(graph_obj, font = "serif", - pointsize = getOption("quickdag.pearl_pointsize"), +theme_qd_pearl <- function(graph_obj, + pointsize = getOption("quickdag.pearl_pointsize"), + pointcolor = getOption("quickdag.pearl_pointcolor"), + pointfill = getOption("quickdag.pearl_pointfill"), linewidths = getOption("quickdag.pearl_linewidths"), - fontsize = getOption("quickdag.pearl_fontsize"), - fontcolor = getOption("quickdag.pearl_fontcolor"), - ...) { + fontname = getOption("quickdag.pearl_fontname"), + fontsize = getOption("quickdag.pearl_fontsize"), + fontcolor = getOption("quickdag.pearl_fontcolor"), + conditioned = NULL) { # set base theme graph_obj <- graph_obj |> theme_qd_base() @@ -102,6 +128,7 @@ theme_qd_pearl <- function(graph_obj, font = "serif", # node attribute tweaks DiagrammeR::add_global_graph_attrs("shape", "point", "node") |> DiagrammeR::add_global_graph_attrs("style", "filled", "node") |> + DiagrammeR::add_global_graph_attrs("color", pointcolor, "node") |> DiagrammeR::add_global_graph_attrs("width", pointsize, "node") |> DiagrammeR::add_global_graph_attrs("height", pointsize, "node") |> DiagrammeR::add_global_graph_attrs("fixedsize", TRUE, "node") |> @@ -112,17 +139,14 @@ theme_qd_pearl <- function(graph_obj, font = "serif", ## Necessary because fillcolor via global attributes appears to be broken ## in DiagrammeR - graph_obj$nodes_df$fillcolor <- "black" + graph_obj$nodes_df$fillcolor <- pointfill ## Add and style external labels graph_obj$nodes_df$xlabel <- graph_obj$nodes_df$label graph_obj$nodes_df$fontcolor <- fontcolor - if ("conditioned" %in% names(match.call())) { - graph_obj <- graph_obj |> get_conditioned_nodes(...) - cd_nodes <- graph_obj$conditioned - graph_obj$nodes_df$label <- "" # Hide internal node labels - } + graph_obj <- graph_obj |> get_conditioned_nodes(conditioned = conditioned) + graph_obj$nodes_df$label <- "" # Hide internal node labels graph_obj$theme <- "pearl" graph_obj diff --git a/R/utils.R b/R/utils.R index e09e0fc..a455cea 100644 --- a/R/utils.R +++ b/R/utils.R @@ -10,8 +10,19 @@ utils::globalVariables(c("alpha_id", "label", "start", "end")) quickdag.format_special = FALSE, quickdag.theme = "base", quickdag.verbose = FALSE, + quickdag.base_linewidths = 0.2, + quickdag.base_fontname = "Helvetica", # DiagrammeR's default + quickdag.base_fontsize = 5, + quickdag.base_fontcolor = "black", + quickdag.circles_linewidths = 0.2, + quickdag.circles_fontname = "Helvetica", # DiagrammeR's default + quickdag.circles_fontsize = 5, + quickdag.circles_fontcolor = "black", quickdag.pearl_pointsize = 0.02, + quickdag.pearl_pointcolor = "black", + quickdag.pearl_pointfill = "black", quickdag.pearl_linewidths = 0.2, + quickdag.pearl_fontname = "Helvetica", # DiagrammeR's default quickdag.pearl_fontsize = 5, quickdag.pearl_fontcolor = "black" ) diff --git a/man/qd_themes.Rd b/man/qd_themes.Rd index 42d3505..1d9882e 100644 --- a/man/qd_themes.Rd +++ b/man/qd_themes.Rd @@ -9,20 +9,36 @@ \alias{cleanup_existing_theme} \title{Diagram themes} \usage{ -qd_themes(graph_obj, theme, ...) +qd_themes(graph_obj, theme, conditioned = NULL) -theme_qd_base(graph_obj, font = "serif", ...) +theme_qd_base( + graph_obj, + linewidths = getOption("quickdag.base_linewidths"), + fontname = getOption("quickdag.base_fontname"), + fontsize = getOption("quickdag.base_fontsize"), + fontcolor = getOption("quickdag.base_fontcolor"), + conditioned = NULL +) -theme_qd_circles(graph_obj, font = "serif", ...) +theme_qd_circles( + graph_obj, + linewidths = getOption("quickdag.circles_linewidths"), + fontname = getOption("quickdag.circles_fontname"), + fontsize = getOption("quickdag.circles_fontsize"), + fontcolor = getOption("quickdag.circles_fontcolor"), + conditioned = NULL +) theme_qd_pearl( graph_obj, - font = "serif", pointsize = getOption("quickdag.pearl_pointsize"), + pointcolor = getOption("quickdag.pearl_pointcolor"), + pointfill = getOption("quickdag.pearl_pointfill"), linewidths = getOption("quickdag.pearl_linewidths"), + fontname = getOption("quickdag.pearl_fontname"), fontsize = getOption("quickdag.pearl_fontsize"), fontcolor = getOption("quickdag.pearl_fontcolor"), - ... + conditioned = NULL ) get_conditioned_nodes(graph_obj, conditioned = NULL) @@ -35,15 +51,31 @@ cleanup_existing_theme(graph_obj) \item{theme}{A character string indicating the theme to use. Defaults to "base". Set to \code{NULL} to use GraphViz defaults.} -\item{...}{Pass arguments to theme call (e.g., \code{\link[=theme_qd_base]{theme_qd_base()}}), such as -\code{conditioned} or \code{font}.} - -\item{font}{A character vector indicating the font family to use for node labels. -Defaults to "serif".} - \item{conditioned}{A character vector indicating which nodes are conditioned upon. The shape for these nodes will be set to "rectangle".} + +\item{linewidths}{Size (in points) of edges and node outlines. By default, controlled +by package options specific to each theme, each of which defaults to 0.2.} + +\item{fontname}{Font name for text elements of the graph. By default, controlled by +package options specific to each theme, each of which defaults to "Helvetica". This +is the default in \code{DiagrammeR}.} + +\item{fontsize}{Size of text (in points). By default, controlled by package options +specific to each theme, each of which defaults to 5.} + +\item{fontcolor}{Text color. By default, controlled by package options +specific to each theme, each of which defaults to "black".} + +\item{pointsize}{Size of "point" shape (in points). Applies to "pearl" theme only and +defaults to 0.02.} + +\item{pointcolor}{Border color for the "point" shape. Applies to "pearl" theme only +and defaults to "black".} + +\item{pointfill}{Fill color for the "point" shape. Applies to "pearl" theme only +and defaults to "black".} } \description{ -Apply various pre-fabricated themes to diagrams. +Themes and theming utilies. } From 08157a802fdfec32dca89426a4f3c547e159e13a Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Mon, 29 Dec 2025 13:42:26 -0600 Subject: [PATCH 22/36] Use underscore for rdname --- R/parse.R | 6 +++--- man/{edge-parsing.Rd => edge_parsing.Rd} | 0 man/{node-parsing.Rd => node_parsing.Rd} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename man/{edge-parsing.Rd => edge_parsing.Rd} (100%) rename man/{node-parsing.Rd => node_parsing.Rd} (100%) diff --git a/R/parse.R b/R/parse.R index 116882b..842928d 100644 --- a/R/parse.R +++ b/R/parse.R @@ -1,6 +1,6 @@ #' @title Parse nodes #' @inheritParams qd_dag -#' @rdname node-parsing +#' @rdname node_parsing #' @export parse_nodes <- function(edgelist) { nodes <- unlist(lapply(edgelist, @@ -12,7 +12,7 @@ parse_nodes <- function(edgelist) { #' @title Parse edges in a graph specification #' @param edgestring A single string containing some set of node-edge relationships -#' @rdname edge-parsing +#' @rdname edge_parsing #' @export parse_edgestring <- function(edgestring) { edgepat <- "\\<\\-\\>|\\-\\>|\\<\\-" @@ -52,7 +52,7 @@ parse_edgestring <- function(edgestring) { Reduce(rbind, edge_mini_dfs) } -#' @rdname edge-parsing +#' @rdname edge_parsing #' @inheritParams qd_dag #' @export parse_edges <- function(edgelist) { diff --git a/man/edge-parsing.Rd b/man/edge_parsing.Rd similarity index 100% rename from man/edge-parsing.Rd rename to man/edge_parsing.Rd diff --git a/man/node-parsing.Rd b/man/node_parsing.Rd similarity index 100% rename from man/node-parsing.Rd rename to man/node_parsing.Rd From 5d1832b3cc7b082167982ae91977932f7cefb704 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Tue, 30 Dec 2025 17:50:06 -0600 Subject: [PATCH 23/36] Update parameter options and documentation --- R/themes.R | 56 +++++++++++++++++++++++++++++++++--------------- R/utils.R | 13 +++++++++-- man/qd_themes.Rd | 36 +++++++++++++++++++++++++++---- 3 files changed, 82 insertions(+), 23 deletions(-) diff --git a/R/themes.R b/R/themes.R index 7f38f84..0b9062a 100644 --- a/R/themes.R +++ b/R/themes.R @@ -8,7 +8,21 @@ #' Set to `NULL` to use GraphViz defaults. #' @param conditioned A character vector indicating which nodes are conditioned upon. #' The shape for these nodes will be set to "rectangle". -#' @param linewidths Size (in points) of edges and node outlines. By default, controlled +#' @param rankdir Direction of node layout. Defaults to "LR", for left-to-right. +#' @param layout Layout engine. Defaults to "dot", which is the only engine explicitly +#' used by [quickdag]. Other options provided by [DiagrammeR] may or may not work +#' well. +#' @param shape Standard node shape for a given graph. Defaults for each theme: +#' "base" = plaintex, "circles" = circle, "pearl" = point. +#' See [Graphviz documentation](https://graphviz.org/doc/info/shapes.html) for other +#' options. +#' @param nodepen Size (in points) of node outlines. By default, controlled +#' by package options specific to each theme, each of which defaults to 0.2. +#' @param nodewidth Width (in inches) of nodes. By default, controlled by package options +#' specific to each theme. Defaults to 0. +#' @param nodeheight Height (in inches) of nodes. By default, controlled by package +#' options specific to each theme. Defaults to 0. +#' @param edgepen Size (in points) of edges. By default, controlled #' by package options specific to each theme, each of which defaults to 0.2. #' @param fontname Font name for text elements of the graph. By default, controlled by #' package options specific to each theme, each of which defaults to "Helvetica". This @@ -53,7 +67,13 @@ qd_themes <- function(graph_obj, theme, conditioned = NULL) { #' @rdname qd_themes #' @export theme_qd_base <- function(graph_obj, - linewidths = getOption("quickdag.base_linewidths"), + rankdir = getOption("quickdag.base_rankdir"), + layout = getOption("quickdag.base_layout"), + shape = getOption("quickdag.base_shape"), + nodepen = getOption("quickdag.base_nodepen"), + nodewidth = getOption("quickdag.base_nodewidth"), + nodeheight = getOption("quickdag.base_nodeheight"), + edgepen = getOption("quickdag.base_edgepen"), fontname = getOption("quickdag.base_fontname"), fontsize = getOption("quickdag.base_fontsize"), fontcolor = getOption("quickdag.base_fontcolor"), @@ -63,19 +83,19 @@ theme_qd_base <- function(graph_obj, graph_attrs <- tibble::tibble( attr = c("rankdir", "layout"), - value = c("LR", "dot"), + value = c(rankdir, layout), attr_type = "graph" ) node_attrs <- tibble::tibble( attr = c("shape", "penwidth", "fontname", "width", "height"), - value = c("plaintext", "0.5", fontname, "0", "0"), + value = c(shape, nodepen, fontname, "0", "0"), attr_type = "node" ) edge_attrs <- tibble::tibble( - attr = c("arrowsize", "penwidth"), - value = c("0.4", "0.5"), + attr = c("arrowsize", "penwidth", "headport", "tailport"), + value = c("0.4", "0.5", "_", "_"), attr_type = "edge" ) @@ -90,10 +110,11 @@ theme_qd_base <- function(graph_obj, #' @rdname qd_themes #' @export theme_qd_circles <- function(graph_obj, - linewidths = getOption("quickdag.circles_linewidths"), - fontname = getOption("quickdag.circles_fontname"), - fontsize = getOption("quickdag.circles_fontsize"), - fontcolor = getOption("quickdag.circles_fontcolor"), + nodepen = getOption("quickdag.circles_nodepen"), + edgepen = getOption("quickdag.circles_edgepen"), + fontname = getOption("quickdag.circles_fontname"), + fontsize = getOption("quickdag.circles_fontsize"), + fontcolor = getOption("quickdag.circles_fontcolor"), conditioned = NULL) { # set base theme @@ -112,13 +133,14 @@ theme_qd_circles <- function(graph_obj, #' @rdname qd_themes #' @export theme_qd_pearl <- function(graph_obj, - pointsize = getOption("quickdag.pearl_pointsize"), - pointcolor = getOption("quickdag.pearl_pointcolor"), - pointfill = getOption("quickdag.pearl_pointfill"), - linewidths = getOption("quickdag.pearl_linewidths"), - fontname = getOption("quickdag.pearl_fontname"), - fontsize = getOption("quickdag.pearl_fontsize"), - fontcolor = getOption("quickdag.pearl_fontcolor"), + pointsize = getOption("quickdag.pearl_pointsize"), + pointcolor = getOption("quickdag.pearl_pointcolor"), + pointfill = getOption("quickdag.pearl_pointfill"), + edgepen = getOption("quickdag.pearl_edgepen"), + arrowsize = getOption("quickdag.pearl_arrowsize"), + fontname = getOption("quickdag.pearl_fontname"), + fontsize = getOption("quickdag.pearl_fontsize"), + fontcolor = getOption("quickdag.pearl_fontcolor"), conditioned = NULL) { # set base theme graph_obj <- graph_obj |> theme_qd_base() diff --git a/R/utils.R b/R/utils.R index a455cea..8d3f1f8 100644 --- a/R/utils.R +++ b/R/utils.R @@ -10,18 +10,27 @@ utils::globalVariables(c("alpha_id", "label", "start", "end")) quickdag.format_special = FALSE, quickdag.theme = "base", quickdag.verbose = FALSE, + quickdag.base_rankdir = "LR", + quickdag.base_layout = "dot", + quickdag.base_shape = "plaintext", + quickdag.base_nodepen = 0.5, + quickdag.base_nodewidth = 0, + quickdag.base_nodeheight = 0, + quickdag.base_edgepen = 0.5, quickdag.base_linewidths = 0.2, quickdag.base_fontname = "Helvetica", # DiagrammeR's default quickdag.base_fontsize = 5, quickdag.base_fontcolor = "black", - quickdag.circles_linewidths = 0.2, + quickdag.circles_nodepen = 0.5, + quickdag.circles_edgepen = 0.5, quickdag.circles_fontname = "Helvetica", # DiagrammeR's default quickdag.circles_fontsize = 5, quickdag.circles_fontcolor = "black", quickdag.pearl_pointsize = 0.02, quickdag.pearl_pointcolor = "black", quickdag.pearl_pointfill = "black", - quickdag.pearl_linewidths = 0.2, + quickdag.pearl_edgepen = 0.2, + quickdag.pearl_arrowsize = 0.2, quickdag.pearl_fontname = "Helvetica", # DiagrammeR's default quickdag.pearl_fontsize = 5, quickdag.pearl_fontcolor = "black" diff --git a/man/qd_themes.Rd b/man/qd_themes.Rd index 1d9882e..3efddb1 100644 --- a/man/qd_themes.Rd +++ b/man/qd_themes.Rd @@ -13,7 +13,13 @@ qd_themes(graph_obj, theme, conditioned = NULL) theme_qd_base( graph_obj, - linewidths = getOption("quickdag.base_linewidths"), + rankdir = getOption("quickdag.base_rankdir"), + layout = getOption("quickdag.base_layout"), + shape = getOption("quickdag.base_shape"), + nodepen = getOption("quickdag.base_nodepen"), + nodewidth = getOption("quickdag.base_nodewidth"), + nodeheight = getOption("quickdag.base_nodeheight"), + edgepen = getOption("quickdag.base_edgepen"), fontname = getOption("quickdag.base_fontname"), fontsize = getOption("quickdag.base_fontsize"), fontcolor = getOption("quickdag.base_fontcolor"), @@ -22,7 +28,8 @@ theme_qd_base( theme_qd_circles( graph_obj, - linewidths = getOption("quickdag.circles_linewidths"), + nodepen = getOption("quickdag.circles_nodepen"), + edgepen = getOption("quickdag.circles_edgepen"), fontname = getOption("quickdag.circles_fontname"), fontsize = getOption("quickdag.circles_fontsize"), fontcolor = getOption("quickdag.circles_fontcolor"), @@ -34,7 +41,8 @@ theme_qd_pearl( pointsize = getOption("quickdag.pearl_pointsize"), pointcolor = getOption("quickdag.pearl_pointcolor"), pointfill = getOption("quickdag.pearl_pointfill"), - linewidths = getOption("quickdag.pearl_linewidths"), + edgepen = getOption("quickdag.pearl_edgepen"), + arrowsize = getOption("quickdag.pearl_arrowsize"), fontname = getOption("quickdag.pearl_fontname"), fontsize = getOption("quickdag.pearl_fontsize"), fontcolor = getOption("quickdag.pearl_fontcolor"), @@ -54,7 +62,27 @@ Set to \code{NULL} to use GraphViz defaults.} \item{conditioned}{A character vector indicating which nodes are conditioned upon. The shape for these nodes will be set to "rectangle".} -\item{linewidths}{Size (in points) of edges and node outlines. By default, controlled +\item{rankdir}{Direction of node layout. Defaults to "LR", for left-to-right.} + +\item{layout}{Layout engine. Defaults to "dot", which is the only engine explicitly +used by \link{quickdag}. Other options provided by \link[DiagrammeR:DiagrammeR]{DiagrammeR::DiagrammeR} may or may not work +well.} + +\item{shape}{Standard node shape for a given graph. Defaults for each theme: +"base" = plaintex, "circles" = circle, "pearl" = point. +See \href{https://graphviz.org/doc/info/shapes.html}{Graphviz documentation} for other +options.} + +\item{nodepen}{Size (in points) of node outlines. By default, controlled +by package options specific to each theme, each of which defaults to 0.2.} + +\item{nodewidth}{Width (in inches) of nodes. By default, controlled by package options +specific to each theme. Defaults to 0.} + +\item{nodeheight}{Height (in inches) of nodes. By default, controlled by package +options specific to each theme. Defaults to 0.} + +\item{edgepen}{Size (in points) of edges. By default, controlled by package options specific to each theme, each of which defaults to 0.2.} \item{fontname}{Font name for text elements of the graph. By default, controlled by From a5238cab8af2cb36f1aedf23f0824302990228ba Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Tue, 30 Dec 2025 17:53:29 -0600 Subject: [PATCH 24/36] Fix bad parameter and set node attributes Switched to DiagrammeR's functions for setting node attributes so that these updates are captured in the graph log --- R/themes.R | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/R/themes.R b/R/themes.R index 0b9062a..edfe22a 100644 --- a/R/themes.R +++ b/R/themes.R @@ -150,25 +150,23 @@ theme_qd_pearl <- function(graph_obj, # node attribute tweaks DiagrammeR::add_global_graph_attrs("shape", "point", "node") |> DiagrammeR::add_global_graph_attrs("style", "filled", "node") |> - DiagrammeR::add_global_graph_attrs("color", pointcolor, "node") |> + DiagrammeR::add_global_graph_attrs("color", pointcolor, "node") |> DiagrammeR::add_global_graph_attrs("width", pointsize, "node") |> DiagrammeR::add_global_graph_attrs("height", pointsize, "node") |> DiagrammeR::add_global_graph_attrs("fixedsize", TRUE, "node") |> DiagrammeR::add_global_graph_attrs("fontsize", fontsize, "node") |> # edge attribute tweaks - DiagrammeR::add_global_graph_attrs("penwidth", linewidths, "edge") |> - DiagrammeR::add_global_graph_attrs("arrowsize", linewidths, "edge") - - ## Necessary because fillcolor via global attributes appears to be broken - ## in DiagrammeR - graph_obj$nodes_df$fillcolor <- pointfill + DiagrammeR::add_global_graph_attrs("penwidth", edgepen, "edge") |> + DiagrammeR::add_global_graph_attrs("arrowsize", arrowsize, "edge") ## Add and style external labels - graph_obj$nodes_df$xlabel <- graph_obj$nodes_df$label - graph_obj$nodes_df$fontcolor <- fontcolor + graph_obj <- graph_obj |> + DiagrammeR::set_node_attrs("fillcolor", pointfill) |> + DiagrammeR::set_node_attrs("xlabel", graph_obj$nodes_df$label) |> + DiagrammeR::set_node_attrs("fontcolor", fontcolor) |> + DiagrammeR::set_node_attrs("label", "") # Nuke internal node labels graph_obj <- graph_obj |> get_conditioned_nodes(conditioned = conditioned) - graph_obj$nodes_df$label <- "" # Hide internal node labels graph_obj$theme <- "pearl" graph_obj From 81786ac1caab46d3996a0b149309261c2ee86401 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Tue, 30 Dec 2025 18:03:39 -0600 Subject: [PATCH 25/36] Add arrowsize for base theme; unset head/tailport --- R/themes.R | 5 +++-- R/utils.R | 2 +- man/qd_themes.Rd | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/R/themes.R b/R/themes.R index edfe22a..325f05a 100644 --- a/R/themes.R +++ b/R/themes.R @@ -74,6 +74,7 @@ theme_qd_base <- function(graph_obj, nodewidth = getOption("quickdag.base_nodewidth"), nodeheight = getOption("quickdag.base_nodeheight"), edgepen = getOption("quickdag.base_edgepen"), + arrowsize = getOption("quickdag.base_arrowsize"), fontname = getOption("quickdag.base_fontname"), fontsize = getOption("quickdag.base_fontsize"), fontcolor = getOption("quickdag.base_fontcolor"), @@ -94,8 +95,8 @@ theme_qd_base <- function(graph_obj, ) edge_attrs <- tibble::tibble( - attr = c("arrowsize", "penwidth", "headport", "tailport"), - value = c("0.4", "0.5", "_", "_"), + attr = c("arrowsize", "penwidth"), + value = c(arrowsize, edgepen), attr_type = "edge" ) diff --git a/R/utils.R b/R/utils.R index 8d3f1f8..ec8df32 100644 --- a/R/utils.R +++ b/R/utils.R @@ -17,7 +17,7 @@ utils::globalVariables(c("alpha_id", "label", "start", "end")) quickdag.base_nodewidth = 0, quickdag.base_nodeheight = 0, quickdag.base_edgepen = 0.5, - quickdag.base_linewidths = 0.2, + quickdag.base_arrowsize = 0.4, quickdag.base_fontname = "Helvetica", # DiagrammeR's default quickdag.base_fontsize = 5, quickdag.base_fontcolor = "black", diff --git a/man/qd_themes.Rd b/man/qd_themes.Rd index 3efddb1..67a7eb7 100644 --- a/man/qd_themes.Rd +++ b/man/qd_themes.Rd @@ -20,6 +20,7 @@ theme_qd_base( nodewidth = getOption("quickdag.base_nodewidth"), nodeheight = getOption("quickdag.base_nodeheight"), edgepen = getOption("quickdag.base_edgepen"), + arrowsize = getOption("quickdag.base_arrowsize"), fontname = getOption("quickdag.base_fontname"), fontsize = getOption("quickdag.base_fontsize"), fontcolor = getOption("quickdag.base_fontcolor"), From ecfc9013d16c38d867bd2c6ac96e99061c371555 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Tue, 30 Dec 2025 19:59:25 -0600 Subject: [PATCH 26/36] Rework default theming Move all node and edge attribute specifications to the node and edge dataframes instead of the global attribute dataframe. --- R/themes.R | 81 +++++++++++++++++++++++------------------------------- 1 file changed, 34 insertions(+), 47 deletions(-) diff --git a/R/themes.R b/R/themes.R index 325f05a..7a0f53c 100644 --- a/R/themes.R +++ b/R/themes.R @@ -80,29 +80,22 @@ theme_qd_base <- function(graph_obj, fontcolor = getOption("quickdag.base_fontcolor"), conditioned = NULL) { - graph_obj <- graph_obj |> cleanup_existing_theme() - - graph_attrs <- tibble::tibble( - attr = c("rankdir", "layout"), - value = c(rankdir, layout), - attr_type = "graph" - ) - - node_attrs <- tibble::tibble( - attr = c("shape", "penwidth", "fontname", "width", "height"), - value = c(shape, nodepen, fontname, "0", "0"), - attr_type = "node" - ) - - edge_attrs <- tibble::tibble( - attr = c("arrowsize", "penwidth"), - value = c(arrowsize, edgepen), - attr_type = "edge" - ) - - graph_obj$global_attrs <- dplyr::bind_rows(graph_attrs, node_attrs, edge_attrs) - - graph_obj <- graph_obj |> get_conditioned_nodes(conditioned = conditioned) + graph_obj <- graph_obj |> + cleanup_existing_theme() |> + DiagrammeR::add_global_graph_attrs("rankdir", "LR", attr_type = "graph") |> + DiagrammeR::add_global_graph_attrs("layout", layout, attr_type = "graph") |> + ## Node aesthetics + DiagrammeR::set_node_attrs("shape", shape) |> + DiagrammeR::set_node_attrs("penwidth", nodepen) |> + DiagrammeR::set_node_attrs("fontname", fontname) |> + DiagrammeR::set_node_attrs("width", nodewidth) |> + DiagrammeR::set_node_attrs("height", nodeheight) |> + DiagrammeR::set_node_attrs("headport", "_") |> + DiagrammeR::set_node_attrs("tailport", "_") |> + ## Edge aesthetics + DiagrammeR::set_edge_attrs("arrowsize", arrowsize) |> + DiagrammeR::set_edge_attrs("penwidth", edgepen) |> + get_conditioned_nodes(conditioned = conditioned) graph_obj$theme <- "base" graph_obj @@ -118,14 +111,11 @@ theme_qd_circles <- function(graph_obj, fontcolor = getOption("quickdag.circles_fontcolor"), conditioned = NULL) { - # set base theme - graph_obj <- graph_obj |> theme_qd_base() - # tweak base theme graph_obj <- graph_obj |> - DiagrammeR::add_global_graph_attrs("shape", "circle", "node") - - graph_obj <- graph_obj |> get_conditioned_nodes(conditioned = conditioned) + theme_qd_base() |> + DiagrammeR::add_global_graph_attrs("shape", "circle", "node") |> + get_conditioned_nodes(conditioned = conditioned) graph_obj$theme <- "circles" graph_obj @@ -143,12 +133,11 @@ theme_qd_pearl <- function(graph_obj, fontsize = getOption("quickdag.pearl_fontsize"), fontcolor = getOption("quickdag.pearl_fontcolor"), conditioned = NULL) { - # set base theme - graph_obj <- graph_obj |> theme_qd_base() - # tweak base theme + # Tweak base theme graph_obj <- graph_obj |> - # node attribute tweaks + theme_qd_base() |> + ## Node attribute tweaks DiagrammeR::add_global_graph_attrs("shape", "point", "node") |> DiagrammeR::add_global_graph_attrs("style", "filled", "node") |> DiagrammeR::add_global_graph_attrs("color", pointcolor, "node") |> @@ -156,18 +145,16 @@ theme_qd_pearl <- function(graph_obj, DiagrammeR::add_global_graph_attrs("height", pointsize, "node") |> DiagrammeR::add_global_graph_attrs("fixedsize", TRUE, "node") |> DiagrammeR::add_global_graph_attrs("fontsize", fontsize, "node") |> - # edge attribute tweaks - DiagrammeR::add_global_graph_attrs("penwidth", edgepen, "edge") |> - DiagrammeR::add_global_graph_attrs("arrowsize", arrowsize, "edge") - - ## Add and style external labels - graph_obj <- graph_obj |> - DiagrammeR::set_node_attrs("fillcolor", pointfill) |> - DiagrammeR::set_node_attrs("xlabel", graph_obj$nodes_df$label) |> - DiagrammeR::set_node_attrs("fontcolor", fontcolor) |> - DiagrammeR::set_node_attrs("label", "") # Nuke internal node labels - - graph_obj <- graph_obj |> get_conditioned_nodes(conditioned = conditioned) + ## Edge attribute tweaks + DiagrammeR::add_global_graph_attrs("penwidth", edgepen, "edge") |> + DiagrammeR::add_global_graph_attrs("arrowsize", arrowsize, "edge") |> + ## Add and style external labels + DiagrammeR::set_node_attrs("fillcolor", pointfill) |> + DiagrammeR::set_node_attrs("xlabel", graph_obj$nodes_df$label) |> + DiagrammeR::set_node_attrs("fontcolor", fontcolor) |> + ## Nuke internal node labels + DiagrammeR::set_node_attrs("label", "") |> + get_conditioned_nodes(conditioned = conditioned) graph_obj$theme <- "pearl" graph_obj @@ -186,11 +173,11 @@ get_conditioned_nodes <- function(graph_obj, conditioned = NULL) { DiagrammeR::get_node_ids(conditions = alpha_id %in% conditioned) graph_obj <- graph_obj |> - # add default columns to node_df based on global_attrs + ## Add default columns to node_df based on global_attrs DiagrammeR::set_node_attrs("shape", default_shape) |> DiagrammeR::set_node_attrs("width", default_minwd) |> DiagrammeR::set_node_attrs("height", default_minht) |> - # select conditioned nodes and update node aesthetics + ## Select conditioned nodes and update node aesthetics DiagrammeR::select_nodes_by_id(cd_nodes) |> DiagrammeR::set_node_attrs_ws("shape", "rectangle") |> DiagrammeR::set_node_attrs_ws("width", "0") |> From aef6a99718bfd9943f84dd37150212b58de5e56b Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Tue, 30 Dec 2025 20:02:07 -0600 Subject: [PATCH 27/36] Fix problem with specifying attributes --- R/graph_operations.R | 13 +++++++------ man/graph_operations.Rd | 17 ++++++----------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/R/graph_operations.R b/R/graph_operations.R index 67c3a46..ed8326b 100644 --- a/R/graph_operations.R +++ b/R/graph_operations.R @@ -2,8 +2,9 @@ #' #' @param graph_obj A `quickdag` object output by [qd_dag()] or [qd_swig()]. #' @param alpha_ids A vector of alphanumeric node IDs upon which to operate. -#' @param node_attr Passed to [DiagrammeR::set_node_attrs()] argument of the same name. -#' @param edge_attr Passed to [DiagrammeR::set_edge_attrs()] argument of the same name. +#' @param ... Passed to [DiagrammeR::set_node_attrs()] or [DiagrammeR::set_edge_attrs()] +#' argument `node_attr` or `edge_attr`, respectively. +#' @param edge_attr Passed to argument of the same name. #' @param values Passed to [DiagrammeR::set_node_attrs()] or #' [DiagrammeR::set_edge_attrs()] argument of the same name. #' @param from_alpha A vector of alphanumeric source node IDs. @@ -13,10 +14,10 @@ #' #' @rdname graph_operations #' @export -qd_set_node_attrs <- function(graph_obj, node_attr, values, alpha_ids) { +qd_set_node_attrs <- function(graph_obj, ..., values, alpha_ids) { numids <- get_numids(graph_obj, alpha_ids) graph_out <- DiagrammeR::set_node_attrs(graph_obj, - node_attr = node_attr, + ..., values = values, nodes = numids) graph_out @@ -24,7 +25,7 @@ qd_set_node_attrs <- function(graph_obj, node_attr, values, alpha_ids) { #' @rdname graph_operations #' @export -qd_set_edge_attrs <- function(graph_obj, edge_attr, values, +qd_set_edge_attrs <- function(graph_obj, ..., values, from_alpha = NULL, to_alpha = NULL) { from_numids <- NULL to_numids <- NULL @@ -35,7 +36,7 @@ qd_set_edge_attrs <- function(graph_obj, edge_attr, values, to_numids <- get_numids(graph_obj, to_alpha) } graph_out <- DiagrammeR::set_edge_attrs(graph_obj, - edge_attr = edge_attr, + ..., values = values, from = from_numids, to = to_numids) diff --git a/man/graph_operations.Rd b/man/graph_operations.Rd index 0ed27a5..9d2885b 100644 --- a/man/graph_operations.Rd +++ b/man/graph_operations.Rd @@ -9,15 +9,9 @@ \alias{qd_select_edges} \title{Graph operations} \usage{ -qd_set_node_attrs(graph_obj, node_attr, values, alpha_ids) +qd_set_node_attrs(graph_obj, ..., values, alpha_ids) -qd_set_edge_attrs( - graph_obj, - edge_attr, - values, - from_alpha = NULL, - to_alpha = NULL -) +qd_set_edge_attrs(graph_obj, ..., values, from_alpha = NULL, to_alpha = NULL) select_nodes_by_alpha_id(graph_obj, alpha_ids, set_op = "union") @@ -30,21 +24,22 @@ qd_select_edges(graph_obj, alpha_ids, set_op = "union") \arguments{ \item{graph_obj}{A \code{quickdag} object output by \code{\link[=qd_dag]{qd_dag()}} or \code{\link[=qd_swig]{qd_swig()}}.} -\item{node_attr}{Passed to \code{\link[DiagrammeR:set_node_attrs]{DiagrammeR::set_node_attrs()}} argument of the same name.} +\item{...}{Passed to \code{\link[DiagrammeR:set_node_attrs]{DiagrammeR::set_node_attrs()}} or \code{\link[DiagrammeR:set_edge_attrs]{DiagrammeR::set_edge_attrs()}} +argument \code{node_attr} or \code{edge_attr}, respectively.} \item{values}{Passed to \code{\link[DiagrammeR:set_node_attrs]{DiagrammeR::set_node_attrs()}} or \code{\link[DiagrammeR:set_edge_attrs]{DiagrammeR::set_edge_attrs()}} argument of the same name.} \item{alpha_ids}{A vector of alphanumeric node IDs upon which to operate.} -\item{edge_attr}{Passed to \code{\link[DiagrammeR:set_edge_attrs]{DiagrammeR::set_edge_attrs()}} argument of the same name.} - \item{from_alpha}{A vector of alphanumeric source node IDs.} \item{to_alpha}{A vector of alphanumeric destination node IDs.} \item{set_op}{Passed to the \code{set_op} argument of \code{\link[DiagrammeR:select_nodes_by_id]{DiagrammeR::select_nodes_by_id()}} or \code{\link[DiagrammeR:select_edges_by_node_id]{DiagrammeR::select_edges_by_node_id()}}. Defaults to "union".} + +\item{edge_attr}{Passed to argument of the same name.} } \description{ Graph operations From 6fab182b045bd100a01c826cbe5133e7d4a437ab Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Tue, 30 Dec 2025 20:22:27 -0600 Subject: [PATCH 28/36] Base theme opts provide shared defaults --- R/themes.R | 42 ++++++++++++++--------------- R/utils.R | 69 +++++++++++++++++++++++++++--------------------- man/qd_themes.Rd | 20 +++++++------- 3 files changed, 70 insertions(+), 61 deletions(-) diff --git a/R/themes.R b/R/themes.R index 7a0f53c..39cec40 100644 --- a/R/themes.R +++ b/R/themes.R @@ -67,17 +67,17 @@ qd_themes <- function(graph_obj, theme, conditioned = NULL) { #' @rdname qd_themes #' @export theme_qd_base <- function(graph_obj, - rankdir = getOption("quickdag.base_rankdir"), - layout = getOption("quickdag.base_layout"), - shape = getOption("quickdag.base_shape"), - nodepen = getOption("quickdag.base_nodepen"), - nodewidth = getOption("quickdag.base_nodewidth"), - nodeheight = getOption("quickdag.base_nodeheight"), - edgepen = getOption("quickdag.base_edgepen"), - arrowsize = getOption("quickdag.base_arrowsize"), - fontname = getOption("quickdag.base_fontname"), - fontsize = getOption("quickdag.base_fontsize"), - fontcolor = getOption("quickdag.base_fontcolor"), + rankdir = getOption("quickdag.base_rankdir"), + layout = getOption("quickdag.base_layout"), + shape = getOption("quickdag.base_shape"), + nodepen = getOption("quickdag.base_nodepen"), + nodewidth = getOption("quickdag.base_nodewidth"), + nodeheight = getOption("quickdag.base_nodeheight"), + edgepen = getOption("quickdag.base_edgepen"), + arrowsize = getOption("quickdag.base_arrowsize"), + fontname = getOption("quickdag.base_fontname"), + fontsize = getOption("quickdag.base_fontsize"), + fontcolor = getOption("quickdag.base_fontcolor"), conditioned = NULL) { graph_obj <- graph_obj |> @@ -104,11 +104,11 @@ theme_qd_base <- function(graph_obj, #' @rdname qd_themes #' @export theme_qd_circles <- function(graph_obj, - nodepen = getOption("quickdag.circles_nodepen"), - edgepen = getOption("quickdag.circles_edgepen"), - fontname = getOption("quickdag.circles_fontname"), - fontsize = getOption("quickdag.circles_fontsize"), - fontcolor = getOption("quickdag.circles_fontcolor"), + nodepen = choose_option("quickdag.circles_nodepen"), + edgepen = choose_option("quickdag.circles_edgepen"), + fontname = choose_option("quickdag.circles_fontname"), + fontsize = choose_option("quickdag.circles_fontsize"), + fontcolor = choose_option("quickdag.circles_fontcolor"), conditioned = NULL) { # tweak base theme @@ -127,11 +127,11 @@ theme_qd_pearl <- function(graph_obj, pointsize = getOption("quickdag.pearl_pointsize"), pointcolor = getOption("quickdag.pearl_pointcolor"), pointfill = getOption("quickdag.pearl_pointfill"), - edgepen = getOption("quickdag.pearl_edgepen"), - arrowsize = getOption("quickdag.pearl_arrowsize"), - fontname = getOption("quickdag.pearl_fontname"), - fontsize = getOption("quickdag.pearl_fontsize"), - fontcolor = getOption("quickdag.pearl_fontcolor"), + edgepen = choose_option("quickdag.pearl_edgepen"), + arrowsize = choose_option("quickdag.pearl_arrowsize"), + fontname = choose_option("quickdag.pearl_fontname"), + fontsize = choose_option("quickdag.pearl_fontsize"), + fontcolor = choose_option("quickdag.pearl_fontcolor"), conditioned = NULL) { # Tweak base theme diff --git a/R/utils.R b/R/utils.R index ec8df32..ef483e5 100644 --- a/R/utils.R +++ b/R/utils.R @@ -4,36 +4,45 @@ utils::globalVariables(c("alpha_id", "label", "start", "end")) # Set global options .onLoad <- function(libname, pkgname) { opts <- list( - quickdag.check_dag = TRUE, - quickdag.embed = FALSE, - quickdag.fixed_sep = "vlin", - quickdag.format_special = FALSE, - quickdag.theme = "base", - quickdag.verbose = FALSE, - quickdag.base_rankdir = "LR", - quickdag.base_layout = "dot", - quickdag.base_shape = "plaintext", - quickdag.base_nodepen = 0.5, - quickdag.base_nodewidth = 0, - quickdag.base_nodeheight = 0, - quickdag.base_edgepen = 0.5, - quickdag.base_arrowsize = 0.4, - quickdag.base_fontname = "Helvetica", # DiagrammeR's default - quickdag.base_fontsize = 5, - quickdag.base_fontcolor = "black", - quickdag.circles_nodepen = 0.5, - quickdag.circles_edgepen = 0.5, - quickdag.circles_fontname = "Helvetica", # DiagrammeR's default - quickdag.circles_fontsize = 5, - quickdag.circles_fontcolor = "black", - quickdag.pearl_pointsize = 0.02, - quickdag.pearl_pointcolor = "black", - quickdag.pearl_pointfill = "black", - quickdag.pearl_edgepen = 0.2, - quickdag.pearl_arrowsize = 0.2, - quickdag.pearl_fontname = "Helvetica", # DiagrammeR's default - quickdag.pearl_fontsize = 5, - quickdag.pearl_fontcolor = "black" + quickdag.check_dag = TRUE, + quickdag.embed = FALSE, + quickdag.fixed_sep = "vlin", + quickdag.format_special = FALSE, + quickdag.theme = "base", + quickdag.verbose = FALSE, + quickdag.base_rankdir = "LR", + quickdag.base_layout = "dot", + quickdag.base_nodewidth = 0, + quickdag.base_shape = "plaintext", + quickdag.base_nodepen = 0.5, + quickdag.base_nodeheight = 0, + quickdag.base_edgepen = 0.5, + quickdag.base_arrowsize = 0.4, + quickdag.base_fontname = "Helvetica", # DiagrammeR's default + quickdag.base_fontsize = 5, + quickdag.base_fontcolor = "black", + quickdag.circles_nodepen = NA, + quickdag.circles_edgepen = NA, + quickdag.circles_fontname = NA, + quickdag.circles_fontsize = NA, + quickdag.circles_fontcolor = NA, + quickdag.pearl_pointsize = 0.02, + quickdag.pearl_pointcolor = "black", + quickdag.pearl_pointfill = "black", + quickdag.pearl_edgepen = NA, + quickdag.pearl_arrowsize = NA, + quickdag.pearl_fontname = NA, + quickdag.pearl_fontsize = NA, + quickdag.pearl_fontcolor = NA ) options(opts) } + +choose_option <- function(x) { + themeopt <- stringr::str_remove(x, "quickdag\\.(circles|pearl)_") + if (is.na(getOption(x))) { + getOption(paste0("quickdag.base_", themeopt)) + } else { + getOption(x) + } +} diff --git a/man/qd_themes.Rd b/man/qd_themes.Rd index 67a7eb7..d060f8c 100644 --- a/man/qd_themes.Rd +++ b/man/qd_themes.Rd @@ -29,11 +29,11 @@ theme_qd_base( theme_qd_circles( graph_obj, - nodepen = getOption("quickdag.circles_nodepen"), - edgepen = getOption("quickdag.circles_edgepen"), - fontname = getOption("quickdag.circles_fontname"), - fontsize = getOption("quickdag.circles_fontsize"), - fontcolor = getOption("quickdag.circles_fontcolor"), + nodepen = choose_option("quickdag.circles_nodepen"), + edgepen = choose_option("quickdag.circles_edgepen"), + fontname = choose_option("quickdag.circles_fontname"), + fontsize = choose_option("quickdag.circles_fontsize"), + fontcolor = choose_option("quickdag.circles_fontcolor"), conditioned = NULL ) @@ -42,11 +42,11 @@ theme_qd_pearl( pointsize = getOption("quickdag.pearl_pointsize"), pointcolor = getOption("quickdag.pearl_pointcolor"), pointfill = getOption("quickdag.pearl_pointfill"), - edgepen = getOption("quickdag.pearl_edgepen"), - arrowsize = getOption("quickdag.pearl_arrowsize"), - fontname = getOption("quickdag.pearl_fontname"), - fontsize = getOption("quickdag.pearl_fontsize"), - fontcolor = getOption("quickdag.pearl_fontcolor"), + edgepen = choose_option("quickdag.pearl_edgepen"), + arrowsize = choose_option("quickdag.pearl_arrowsize"), + fontname = choose_option("quickdag.pearl_fontname"), + fontsize = choose_option("quickdag.pearl_fontsize"), + fontcolor = choose_option("quickdag.pearl_fontcolor"), conditioned = NULL ) From a3c1e1af0cbea1db713d5f37db90a35e41f5d492 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Tue, 30 Dec 2025 20:38:53 -0600 Subject: [PATCH 29/36] Add option to select arrowhead shape --- R/themes.R | 32 ++++++++++++++++++-------------- R/utils.R | 4 +++- man/qd_themes.Rd | 2 ++ 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/R/themes.R b/R/themes.R index 39cec40..063c943 100644 --- a/R/themes.R +++ b/R/themes.R @@ -74,6 +74,7 @@ theme_qd_base <- function(graph_obj, nodewidth = getOption("quickdag.base_nodewidth"), nodeheight = getOption("quickdag.base_nodeheight"), edgepen = getOption("quickdag.base_edgepen"), + arrowhead = getOption("quickdag.base_arrowhead"), arrowsize = getOption("quickdag.base_arrowsize"), fontname = getOption("quickdag.base_fontname"), fontsize = getOption("quickdag.base_fontsize"), @@ -93,6 +94,7 @@ theme_qd_base <- function(graph_obj, DiagrammeR::set_node_attrs("headport", "_") |> DiagrammeR::set_node_attrs("tailport", "_") |> ## Edge aesthetics + DiagrammeR::set_edge_attrs("arrowhead", arrowhead) |> DiagrammeR::set_edge_attrs("arrowsize", arrowsize) |> DiagrammeR::set_edge_attrs("penwidth", edgepen) |> get_conditioned_nodes(conditioned = conditioned) @@ -128,6 +130,7 @@ theme_qd_pearl <- function(graph_obj, pointcolor = getOption("quickdag.pearl_pointcolor"), pointfill = getOption("quickdag.pearl_pointfill"), edgepen = choose_option("quickdag.pearl_edgepen"), + arrowhead = choose_option("quickdag.pearl_arrowhead"), arrowsize = choose_option("quickdag.pearl_arrowsize"), fontname = choose_option("quickdag.pearl_fontname"), fontsize = choose_option("quickdag.pearl_fontsize"), @@ -138,22 +141,23 @@ theme_qd_pearl <- function(graph_obj, graph_obj <- graph_obj |> theme_qd_base() |> ## Node attribute tweaks - DiagrammeR::add_global_graph_attrs("shape", "point", "node") |> - DiagrammeR::add_global_graph_attrs("style", "filled", "node") |> - DiagrammeR::add_global_graph_attrs("color", pointcolor, "node") |> - DiagrammeR::add_global_graph_attrs("width", pointsize, "node") |> - DiagrammeR::add_global_graph_attrs("height", pointsize, "node") |> - DiagrammeR::add_global_graph_attrs("fixedsize", TRUE, "node") |> - DiagrammeR::add_global_graph_attrs("fontsize", fontsize, "node") |> - ## Edge attribute tweaks - DiagrammeR::add_global_graph_attrs("penwidth", edgepen, "edge") |> - DiagrammeR::add_global_graph_attrs("arrowsize", arrowsize, "edge") |> + DiagrammeR::set_node_attrs("shape", "point") |> + DiagrammeR::set_node_attrs("style", "filled") |> + DiagrammeR::set_node_attrs("color", pointcolor) |> + DiagrammeR::set_node_attrs("width", pointsize) |> + DiagrammeR::set_node_attrs("height", pointsize) |> + DiagrammeR::set_node_attrs("fixedsize", TRUE) |> + DiagrammeR::set_node_attrs("fontsize", fontsize) |> ## Add and style external labels - DiagrammeR::set_node_attrs("fillcolor", pointfill) |> - DiagrammeR::set_node_attrs("xlabel", graph_obj$nodes_df$label) |> - DiagrammeR::set_node_attrs("fontcolor", fontcolor) |> + DiagrammeR::set_node_attrs("fillcolor", pointfill) |> + DiagrammeR::set_node_attrs("xlabel", graph_obj$nodes_df$label) |> ## Nuke internal node labels - DiagrammeR::set_node_attrs("label", "") |> + DiagrammeR::set_node_attrs("fontcolor", fontcolor) |> + DiagrammeR::set_node_attrs("label", "") |> + ## Edge attribute tweaks + DiagrammeR::set_edge_attrs("penwidth", edgepen) |> + DiagrammeR::set_edge_attrs("arrowhead", arrowhead) |> + DiagrammeR::set_edge_attrs("arrowsize", arrowsize) |> get_conditioned_nodes(conditioned = conditioned) graph_obj$theme <- "pearl" diff --git a/R/utils.R b/R/utils.R index ef483e5..2d5cfad 100644 --- a/R/utils.R +++ b/R/utils.R @@ -17,6 +17,7 @@ utils::globalVariables(c("alpha_id", "label", "start", "end")) quickdag.base_nodepen = 0.5, quickdag.base_nodeheight = 0, quickdag.base_edgepen = 0.5, + quickdag.base_arrowhead = "normal", quickdag.base_arrowsize = 0.4, quickdag.base_fontname = "Helvetica", # DiagrammeR's default quickdag.base_fontsize = 5, @@ -30,7 +31,8 @@ utils::globalVariables(c("alpha_id", "label", "start", "end")) quickdag.pearl_pointcolor = "black", quickdag.pearl_pointfill = "black", quickdag.pearl_edgepen = NA, - quickdag.pearl_arrowsize = NA, + quickdag.pearl_arrowhead = NA, + quickdag.pearl_arrowsize = 0.2, quickdag.pearl_fontname = NA, quickdag.pearl_fontsize = NA, quickdag.pearl_fontcolor = NA diff --git a/man/qd_themes.Rd b/man/qd_themes.Rd index d060f8c..f627a94 100644 --- a/man/qd_themes.Rd +++ b/man/qd_themes.Rd @@ -20,6 +20,7 @@ theme_qd_base( nodewidth = getOption("quickdag.base_nodewidth"), nodeheight = getOption("quickdag.base_nodeheight"), edgepen = getOption("quickdag.base_edgepen"), + arrowhead = getOption("quickdag.base_arrowhead"), arrowsize = getOption("quickdag.base_arrowsize"), fontname = getOption("quickdag.base_fontname"), fontsize = getOption("quickdag.base_fontsize"), @@ -43,6 +44,7 @@ theme_qd_pearl( pointcolor = getOption("quickdag.pearl_pointcolor"), pointfill = getOption("quickdag.pearl_pointfill"), edgepen = choose_option("quickdag.pearl_edgepen"), + arrowhead = choose_option("quickdag.pearl_arrowhead"), arrowsize = choose_option("quickdag.pearl_arrowsize"), fontname = choose_option("quickdag.pearl_fontname"), fontsize = choose_option("quickdag.pearl_fontsize"), From b398c0d9cd5db9346bd4cd3944fdcb5594f4462d Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Tue, 30 Dec 2025 20:52:16 -0600 Subject: [PATCH 30/36] Revert to dots to pass values to set functions --- R/graph_operations.R | 6 ++---- man/graph_operations.Rd | 10 +++------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/R/graph_operations.R b/R/graph_operations.R index ed8326b..5e21f8d 100644 --- a/R/graph_operations.R +++ b/R/graph_operations.R @@ -4,8 +4,6 @@ #' @param alpha_ids A vector of alphanumeric node IDs upon which to operate. #' @param ... Passed to [DiagrammeR::set_node_attrs()] or [DiagrammeR::set_edge_attrs()] #' argument `node_attr` or `edge_attr`, respectively. -#' @param edge_attr Passed to argument of the same name. -#' @param values Passed to [DiagrammeR::set_node_attrs()] or #' [DiagrammeR::set_edge_attrs()] argument of the same name. #' @param from_alpha A vector of alphanumeric source node IDs. #' @param to_alpha A vector of alphanumeric destination node IDs. @@ -14,7 +12,7 @@ #' #' @rdname graph_operations #' @export -qd_set_node_attrs <- function(graph_obj, ..., values, alpha_ids) { +qd_set_node_attrs <- function(graph_obj, ..., alpha_ids) { numids <- get_numids(graph_obj, alpha_ids) graph_out <- DiagrammeR::set_node_attrs(graph_obj, ..., @@ -25,7 +23,7 @@ qd_set_node_attrs <- function(graph_obj, ..., values, alpha_ids) { #' @rdname graph_operations #' @export -qd_set_edge_attrs <- function(graph_obj, ..., values, +qd_set_edge_attrs <- function(graph_obj, ..., from_alpha = NULL, to_alpha = NULL) { from_numids <- NULL to_numids <- NULL diff --git a/man/graph_operations.Rd b/man/graph_operations.Rd index 9d2885b..da5cc47 100644 --- a/man/graph_operations.Rd +++ b/man/graph_operations.Rd @@ -9,9 +9,9 @@ \alias{qd_select_edges} \title{Graph operations} \usage{ -qd_set_node_attrs(graph_obj, ..., values, alpha_ids) +qd_set_node_attrs(graph_obj, ..., alpha_ids) -qd_set_edge_attrs(graph_obj, ..., values, from_alpha = NULL, to_alpha = NULL) +qd_set_edge_attrs(graph_obj, ..., from_alpha = NULL, to_alpha = NULL) select_nodes_by_alpha_id(graph_obj, alpha_ids, set_op = "union") @@ -25,9 +25,7 @@ qd_select_edges(graph_obj, alpha_ids, set_op = "union") \item{graph_obj}{A \code{quickdag} object output by \code{\link[=qd_dag]{qd_dag()}} or \code{\link[=qd_swig]{qd_swig()}}.} \item{...}{Passed to \code{\link[DiagrammeR:set_node_attrs]{DiagrammeR::set_node_attrs()}} or \code{\link[DiagrammeR:set_edge_attrs]{DiagrammeR::set_edge_attrs()}} -argument \code{node_attr} or \code{edge_attr}, respectively.} - -\item{values}{Passed to \code{\link[DiagrammeR:set_node_attrs]{DiagrammeR::set_node_attrs()}} or +argument \code{node_attr} or \code{edge_attr}, respectively. \code{\link[DiagrammeR:set_edge_attrs]{DiagrammeR::set_edge_attrs()}} argument of the same name.} \item{alpha_ids}{A vector of alphanumeric node IDs upon which to operate.} @@ -38,8 +36,6 @@ argument \code{node_attr} or \code{edge_attr}, respectively.} \item{set_op}{Passed to the \code{set_op} argument of \code{\link[DiagrammeR:select_nodes_by_id]{DiagrammeR::select_nodes_by_id()}} or \code{\link[DiagrammeR:select_edges_by_node_id]{DiagrammeR::select_edges_by_node_id()}}. Defaults to "union".} - -\item{edge_attr}{Passed to argument of the same name.} } \description{ Graph operations From ff537c8eb1355b8d0d886e772a568e9086592429 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Tue, 30 Dec 2025 21:05:50 -0600 Subject: [PATCH 31/36] Tweak and add separate options for SWIGs --- R/qd_swig.R | 4 ++-- R/utils.R | 3 ++- man/qd_swig.Rd | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/R/qd_swig.R b/R/qd_swig.R index dd38569..7ee1cce 100644 --- a/R/qd_swig.R +++ b/R/qd_swig.R @@ -32,8 +32,8 @@ qd_swig <- function(graph_obj, fixed_nodes, custom_values = NULL, - fixed_sep = getOption("quickdag.fixed_sep"), - sep_point_size = 15) { + fixed_sep = getOption("quickdag.swig_fixedsep"), + sep_point_size = getOption("quickdag.swig_sepsize")) { ndf <- DiagrammeR::get_node_df(graph_obj) ndf$fixed <- with(ndf, ifelse(alpha_id %in% fixed_nodes, TRUE, FALSE)) diff --git a/R/utils.R b/R/utils.R index 2d5cfad..e9a04d5 100644 --- a/R/utils.R +++ b/R/utils.R @@ -6,7 +6,8 @@ utils::globalVariables(c("alpha_id", "label", "start", "end")) opts <- list( quickdag.check_dag = TRUE, quickdag.embed = FALSE, - quickdag.fixed_sep = "vlin", + quickdag.swig_fixedsep = "vlin", + quickdag.swig_sepsize = 15, quickdag.format_special = FALSE, quickdag.theme = "base", quickdag.verbose = FALSE, diff --git a/man/qd_swig.Rd b/man/qd_swig.Rd index 72272da..767fb04 100644 --- a/man/qd_swig.Rd +++ b/man/qd_swig.Rd @@ -8,8 +8,8 @@ qd_swig( graph_obj, fixed_nodes, custom_values = NULL, - fixed_sep = getOption("quickdag.fixed_sep"), - sep_point_size = 15 + fixed_sep = getOption("quickdag.swig_fixedsep"), + sep_point_size = getOption("quickdag.swig_sepsize") ) } \arguments{ From 4354297acd179490dfcfce7084cc47f1db1d67dd Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 19 Apr 2026 22:03:32 -0400 Subject: [PATCH 32/36] Add docs for missing arrow property parameters --- R/themes.R | 2 ++ man/qd_themes.Rd | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/R/themes.R b/R/themes.R index 063c943..29c733b 100644 --- a/R/themes.R +++ b/R/themes.R @@ -16,6 +16,8 @@ #' "base" = plaintex, "circles" = circle, "pearl" = point. #' See [Graphviz documentation](https://graphviz.org/doc/info/shapes.html) for other #' options. +#' @param arrowhead Shape of the arrowhead. See [DiagrammeR::edge_aes()]. +#' @param arrowsize Size of the arrowhead. See [DiagrammeR::edge_aes()]. #' @param nodepen Size (in points) of node outlines. By default, controlled #' by package options specific to each theme, each of which defaults to 0.2. #' @param nodewidth Width (in inches) of nodes. By default, controlled by package options diff --git a/man/qd_themes.Rd b/man/qd_themes.Rd index f627a94..6f298c6 100644 --- a/man/qd_themes.Rd +++ b/man/qd_themes.Rd @@ -88,6 +88,10 @@ options specific to each theme. Defaults to 0.} \item{edgepen}{Size (in points) of edges. By default, controlled by package options specific to each theme, each of which defaults to 0.2.} +\item{arrowhead}{Shape of the arrowhead. See \code{\link[DiagrammeR:edge_aes]{DiagrammeR::edge_aes()}}.} + +\item{arrowsize}{Size of the arrowhead. See \code{\link[DiagrammeR:edge_aes]{DiagrammeR::edge_aes()}}.} + \item{fontname}{Font name for text elements of the graph. By default, controlled by package options specific to each theme, each of which defaults to "Helvetica". This is the default in \code{DiagrammeR}.} From ad899b42cc4c472fc8632b5425a96e32e29bc3d8 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 19 Apr 2026 22:06:02 -0400 Subject: [PATCH 33/36] Remove 'messaging' package --- DESCRIPTION | 1 - 1 file changed, 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index aa32e0d..a7414d2 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,6 @@ Imports: dagitty (>= 0.3.4), DiagrammeR (>= 1.0.11), dplyr (>= 1.1.4), - messaging (>= 0.1.0), purrr (>= 1.1.0), rlang (>= 1.1.6), stringr (>= 1.5.1), From 294e17fa68bb7467a3fcbe6a3121ebd9cdbb6218 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 19 Apr 2026 22:08:19 -0400 Subject: [PATCH 34/36] Add 'id' to global variables --- R/utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/utils.R b/R/utils.R index e9a04d5..9c2ccbc 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,5 +1,5 @@ # Put some variables in the global environment to avoid R CMD CHECK notes -utils::globalVariables(c("alpha_id", "label", "start", "end")) +utils::globalVariables(c("alpha_id", "label", "start", "end", "id")) # Set global options .onLoad <- function(libname, pkgname) { From 7e8ebaceb30fdbf550a5c2afc219e0842a6d4318 Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 19 Apr 2026 22:11:14 -0400 Subject: [PATCH 35/36] Add 'values' to global variables --- R/utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/utils.R b/R/utils.R index 9c2ccbc..3199998 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,5 +1,5 @@ # Put some variables in the global environment to avoid R CMD CHECK notes -utils::globalVariables(c("alpha_id", "label", "start", "end", "id")) +utils::globalVariables(c("alpha_id", "label", "start", "end", "id", "values")) # Set global options .onLoad <- function(libname, pkgname) { From c67a7cc75dc42be37887e1cf4ad14e7ef28f750a Mon Sep 17 00:00:00 2001 From: Jason Gantenberg Date: Sun, 19 Apr 2026 22:15:36 -0400 Subject: [PATCH 36/36] Fix adjustment set example --- R/qd_todagitty.R | 2 +- man/qd_adjustment_sets.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/qd_todagitty.R b/R/qd_todagitty.R index 69e4b05..69b1f06 100644 --- a/R/qd_todagitty.R +++ b/R/qd_todagitty.R @@ -31,7 +31,7 @@ #' #' # if you've already created a qd_dag() object #' dag <- qd_dag(edges) -#' qd_adjustment_sets(dag$qd_edgelist) +#' qd_adjustment_sets(dag$qd_edgelist, exposure = "A", outcome = "Y") qd_adjustment_sets <- function(edgelist, diagram_type = "dag", showplot = FALSE, exposure, outcome, ...) { diff --git a/man/qd_adjustment_sets.Rd b/man/qd_adjustment_sets.Rd index 0aa96ae..d0ff60a 100644 --- a/man/qd_adjustment_sets.Rd +++ b/man/qd_adjustment_sets.Rd @@ -58,5 +58,5 @@ qd_adjustment_sets(edges, exposure = "A", outcome = "C", type = "minimal") # if you've already created a qd_dag() object dag <- qd_dag(edges) -qd_adjustment_sets(dag$qd_edgelist) +qd_adjustment_sets(dag$qd_edgelist, exposure = "A", outcome = "Y") }