suppressPackageStartupMessages({ library(argparse) library(ggplot2) library(dplyr) library(tidyr) library(readr) library(scales) library(tikzDevice) }) SOLUTION_ORDER <- c("tso", "tso-pacing", "cake") SOLUTION_LABELS <- c( "no-tso" = "TSO Off", "tso" = "TSO On", "tso-pacing" = "TSO Pacing", "cake" = "Cake" ) SOLUTION_COLORS <- c( "no-tso" = "#009E73", "tso" = "#E69F00", "tso-pacing" = "#0072B2", "cake" = "#CC79A7" ) SOLUTION_LINETYPES <- c( "no-tso" = "longdash", "tso" = "dashed", "tso-pacing" = "solid", "cake" = "twodash" ) # Global figure size and font size, overridable via env vars # (Makefile exports FIG_WIDTH / FIG_HEIGHT / FIG_FONTSIZE). .env_num <- function(var, default) { v <- Sys.getenv(var) if (nchar(v) == 0) default else as.numeric(v) } DEFAULT_WIDTH <- .env_num("FIG_WIDTH", 3.33) DEFAULT_HEIGHT <- .env_num("FIG_HEIGHT", 2.4) DEFAULT_FONTSIZE <- .env_num("FIG_FONTSIZE", 10) theme_paper <- function(base_size = DEFAULT_FONTSIZE) { theme_bw(base_size = base_size) + theme( legend.position = "bottom", legend.title = element_blank(), legend.margin = margin(t = -4, l = -20), legend.text = element_text(size = base_size - 1), legend.key.size = unit(base_size, "pt"), axis.text = element_text(size = base_size - 1), strip.background = element_rect(fill = "grey90"), panel.grid.minor = element_blank(), plot.margin = margin(2, 4, 2, 2) ) } prepare_solution <- function(df) { df %>% filter(solution %in% SOLUTION_ORDER) %>% mutate(solution = factor(solution, levels = SOLUTION_ORDER, labels = SOLUTION_LABELS[SOLUTION_ORDER])) } LABEL_COLORS <- setNames(SOLUTION_COLORS[SOLUTION_ORDER], SOLUTION_LABELS[SOLUTION_ORDER]) LABEL_LINETYPES <- setNames(SOLUTION_LINETYPES[SOLUTION_ORDER], SOLUTION_LABELS[SOLUTION_ORDER]) COL_WIDTH <- 3.33 TEXT_WIDTH <- 7.0 # ACM page-fraction presets, in inches. HALF_WIDTH <- 3.3 # two figures side-by-side under one \begin{figure} THIRD_WIDTH <- 2.2 # three figures side-by-side label_pct <- function() percent_format(accuracy = 1) label_us <- function() "µs" pct_sign <- function() "%" fig_parser <- function(default_width = DEFAULT_WIDTH, default_height = DEFAULT_HEIGHT, description = "Generate figure") { p <- ArgumentParser(description = description) p$add_argument("--data", required = TRUE, help = "derived CSV directory") p$add_argument("--pdf", help = "write PDF to this path") p$add_argument("--tikz", help = "write tikz .tex to this path") p$add_argument("--width", type = "double", default = default_width) p$add_argument("--height", type = "double", default = default_height) p$add_argument("--zoom", action = "store_true", help = "use zoomed axis limits") p$add_argument("--debug-margins", action = "store_true", help = "color plot.background / panel.background / panel.border to visualise layout") p } # Optional theme overlay that colors the plot canvas, plot panel, and panel # border so layout / whitespace can be inspected. Returns NULL when off # (adding NULL to a ggplot is a no-op). theme_debug_margins <- function(args) { if (!isTRUE(args$debug_margins)) return(NULL) theme( plot.background = element_rect(fill = "#FFF7B3", colour = "black"), panel.background = element_rect(fill = "#CCE5FF", colour = NA), panel.border = element_rect(colour = "red", fill = NA, linewidth = 0.6) ) } save_figure <- function(plot, args) { if (is.null(args$pdf) && is.null(args$tikz)) { stop("save_figure: pass --pdf and/or --tikz") } if (!is.null(args$pdf)) { dir.create(dirname(args$pdf), showWarnings = FALSE, recursive = TRUE) ggsave(args$pdf, plot, width = args$width, height = args$height, device = "pdf") message("wrote ", args$pdf) } if (!is.null(args$tikz)) { dir.create(dirname(args$tikz), showWarnings = FALSE, recursive = TRUE) old <- options( tikzSanitizeCharacters = c(getOption("tikzSanitizeCharacters"), "µ"), tikzReplacementCharacters = c(getOption("tikzReplacementCharacters"), "$\\mu$") ) on.exit(options(old), add = TRUE) tikz(args$tikz, width = args$width, height = args$height, standAlone = FALSE, sanitize = TRUE) print(plot) dev.off() message("wrote ", args$tikz) } }