{"spec_id":"arc-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' arc-basic: Basic Arc Diagram\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 83/100 | Created: 2026-05-30\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(tibble)\nlibrary(tidyr)\nlibrary(ragg)\n\nset.seed(42)\n\n# Theme tokens (Imprint palette)\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nELEVATED_BG <- if (THEME == \"light\") \"#FFFDF6\" else \"#242420\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\nIMPRINT_PALETTE <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n                     \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\n\n# Nodes: Hamlet characters ordered by narrative prominence\nnode_names <- c(\"Hamlet\", \"Horatio\", \"Claudius\", \"Gertrude\", \"Ophelia\",\n                \"Polonius\", \"Laertes\", \"Marcellus\", \"Rosencrantz\", \"Guildenstern\")\nnode_df <- tibble(name = node_names, x = seq_along(node_names))\n\n# Edges with relationship type\nedges_df <- tribble(\n  ~from,         ~to,            ~type,\n  \"Hamlet\",      \"Horatio\",      \"Friendship\",\n  \"Hamlet\",      \"Claudius\",     \"Political\",\n  \"Hamlet\",      \"Gertrude\",     \"Family\",\n  \"Hamlet\",      \"Ophelia\",      \"Romantic\",\n  \"Hamlet\",      \"Polonius\",     \"Political\",\n  \"Hamlet\",      \"Laertes\",      \"Political\",\n  \"Hamlet\",      \"Marcellus\",    \"Friendship\",\n  \"Hamlet\",      \"Rosencrantz\",  \"Friendship\",\n  \"Hamlet\",      \"Guildenstern\", \"Friendship\",\n  \"Claudius\",    \"Gertrude\",     \"Romantic\",\n  \"Claudius\",    \"Polonius\",     \"Political\",\n  \"Claudius\",    \"Rosencrantz\",  \"Political\",\n  \"Claudius\",    \"Guildenstern\", \"Political\",\n  \"Ophelia\",     \"Polonius\",     \"Family\",\n  \"Ophelia\",     \"Laertes\",      \"Family\",\n  \"Polonius\",    \"Laertes\",      \"Family\",\n  \"Horatio\",     \"Marcellus\",    \"Friendship\"\n) %>%\n  left_join(node_df, by = c(\"from\" = \"name\")) %>%\n  rename(x1 = x) %>%\n  left_join(node_df, by = c(\"to\" = \"name\")) %>%\n  rename(x2 = x) %>%\n  mutate(edge_id = row_number())\n\n# Semi-ellipse arcs: height proportional to span distance, inlined per-row\narc_data <- edges_df %>%\n  rowwise() %>%\n  mutate(pts = list({\n    cx <- (x1 + x2) / 2\n    r  <- abs(x2 - x1) / 2\n    t  <- seq(pi, 0, length.out = 80)\n    tibble(arc_x = cx + r * cos(t), arc_y = 0.55 * r * sin(t))\n  })) %>%\n  ungroup() %>%\n  unnest(cols = c(pts))\n\n# Title: 36 chars < 67 baseline, so full default size\nplot_title <- \"arc-basic · r · ggplot2 · anyplot.ai\"\nn_chars    <- nchar(plot_title)\ntitle_size <- max(8L, round(12 * min(1.0, 67 / n_chars)))\n\n# Relationship colors — Imprint palette positions 1–4\nrel_colors <- c(\n  \"Family\"     = IMPRINT_PALETTE[1],  # #009E73 brand green\n  \"Friendship\" = IMPRINT_PALETTE[2],  # #C475FD lavender\n  \"Political\"  = IMPRINT_PALETTE[3],  # #4467A3 blue\n  \"Romantic\"   = IMPRINT_PALETTE[4]   # #BD8233 ochre\n)\n\np <- ggplot() +\n  # Arcs above the baseline\n  geom_path(\n    data = arc_data,\n    aes(x = arc_x, y = arc_y, group = edge_id, color = type),\n    linewidth = 0.85, alpha = 0.60\n  ) +\n  # Horizontal baseline\n  geom_hline(yintercept = 0, color = INK_SOFT, linewidth = 0.35) +\n  # Nodes as filled circles on the baseline\n  geom_point(\n    data = node_df, aes(x = x, y = 0),\n    shape = 21, size = 3.0, fill = INK, color = PAGE_BG, stroke = 0.8\n  ) +\n  # Node labels rotated below baseline\n  geom_text(\n    data = node_df, aes(x = x, y = -0.15, label = name),\n    color = INK_SOFT, size = 3.0, angle = 40, hjust = 1, vjust = 1\n  ) +\n  scale_color_manual(values = rel_colors, name = \"Relationship\") +\n  scale_x_continuous(expand = expansion(mult = c(0.04, 0.12))) +\n  coord_cartesian(ylim = c(-0.8, 2.8), clip = \"off\") +\n  labs(title = plot_title) +\n  theme_void() +\n  theme(\n    plot.background   = element_rect(fill = PAGE_BG, color = PAGE_BG),\n    panel.background  = element_rect(fill = PAGE_BG, color = NA),\n    plot.title        = element_text(color = INK, size = title_size,\n                                      hjust = 0.5, face = \"bold\",\n                                      margin = margin(t = 10, b = 6)),\n    legend.position   = \"bottom\",\n    legend.direction  = \"horizontal\",\n    legend.text       = element_text(color = INK_SOFT, size = 8),\n    legend.title      = element_text(color = INK, size = 9, face = \"bold\"),\n    legend.background = element_rect(fill = ELEVATED_BG, color = NA),\n    legend.key        = element_rect(fill = NA, color = NA),\n    legend.margin     = margin(2, 8, 4, 8),\n    plot.margin       = margin(20, 20, 10, 20)\n  )\n\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 8,\n  height   = 4.5,\n  units    = \"in\",\n  dpi      = 400\n)\n"}