{"spec_id":"gantt-dependencies","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' gantt-dependencies: Gantt Chart with Dependencies\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 88/100 | Created: 2026-06-02\n\nlibrary(ggplot2)\nlibrary(ragg)\n\n# --- Theme tokens ---\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\"\nINK_MUTED   <- if (THEME == \"light\") \"#6B6A63\" else \"#A8A79F\"\n\nIMPRINT_PALETTE <- c(\n  \"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n  \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"\n)\n\nPHASE_COLORS <- c(\n  \"Requirements\" = IMPRINT_PALETTE[1],\n  \"Design\"       = IMPRINT_PALETTE[2],\n  \"Development\"  = IMPRINT_PALETTE[3],\n  \"Testing\"      = IMPRINT_PALETTE[4]\n)\n\n# --- Data ---\n# Software development project: tasks with y positions (top = high y)\ntasks <- data.frame(\n  task_id = 1:9,\n  label   = c(\"Stakeholder Interviews\", \"Requirements Doc\",\n              \"UI Wireframes\", \"System Architecture\",\n              \"Frontend Development\", \"Backend Development\",\n              \"API Integration\", \"Unit Testing\", \"UAT\"),\n  phase   = c(\"Requirements\", \"Requirements\",\n              \"Design\", \"Design\",\n              \"Development\", \"Development\", \"Development\",\n              \"Testing\", \"Testing\"),\n  start   = as.Date(c(\"2024-01-08\", \"2024-01-29\",\n                      \"2024-02-19\", \"2024-02-19\",\n                      \"2024-03-18\", \"2024-03-11\",\n                      \"2024-04-15\",\n                      \"2024-05-13\", \"2024-06-10\")),\n  end     = as.Date(c(\"2024-01-19\", \"2024-02-09\",\n                      \"2024-03-08\", \"2024-03-01\",\n                      \"2024-04-05\", \"2024-03-29\",\n                      \"2024-05-03\",\n                      \"2024-05-31\", \"2024-06-28\")),\n  y_pos   = c(12, 11, 9, 8, 6, 5, 4, 2, 1),\n  stringsAsFactors = FALSE\n)\n\n# Phase aggregate bars (span earliest start to latest end per phase)\nphases <- data.frame(\n  label = c(\"Requirements\", \"Design\", \"Development\", \"Testing\"),\n  phase = c(\"Requirements\", \"Design\", \"Development\", \"Testing\"),\n  start = as.Date(c(\"2024-01-08\", \"2024-02-19\", \"2024-03-11\", \"2024-05-13\")),\n  end   = as.Date(c(\"2024-02-09\", \"2024-03-08\", \"2024-05-03\", \"2024-06-28\")),\n  y_pos = c(13, 10, 7, 3),\n  stringsAsFactors = FALSE\n)\n\n# Y-axis breaks: all 13 rows (phases uppercase, tasks indented)\nall_y_pos <- c(phases$y_pos, tasks$y_pos)\nall_y_lab <- c(toupper(phases$label), paste0(\"  \", tasks$label))\ny_ord     <- order(all_y_pos)\nall_y_pos <- all_y_pos[y_ord]\nall_y_lab <- all_y_lab[y_ord]\n\n# Dependencies: finish-to-start (from task_id -> to task_id)\ndep_from <- c(2, 2, 3, 4, 5, 6, 7, 8)\ndep_to   <- c(3, 4, 5, 6, 7, 7, 8, 9)\n# Critical path: Requirements Doc -> UI Wireframes -> Frontend -> API -> Unit Testing -> UAT\nis_critical <- c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE)\n\n# Build 3-segment L-shaped dependency arrows\noffset_days <- 3\nseg1_list <- vector(\"list\", length(dep_from))\nseg2_list <- vector(\"list\", length(dep_from))\nseg3_list <- vector(\"list\", length(dep_from))\n\nfor (i in seq_along(dep_from)) {\n  from_row <- tasks[tasks$task_id == dep_from[i], ]\n  to_row   <- tasks[tasks$task_id == dep_to[i], ]\n  x1 <- from_row$end\n  y1 <- from_row$y_pos\n  x2 <- to_row$start\n  y2 <- to_row$y_pos\n  mx <- x1 + offset_days\n  crit <- is_critical[i]\n  seg1_list[[i]] <- data.frame(x = x1, xend = mx, y = y1, yend = y1, critical = crit)\n  seg2_list[[i]] <- data.frame(x = mx, xend = mx, y = y1, yend = y2, critical = crit)\n  seg3_list[[i]] <- data.frame(x = mx, xend = x2, y = y2, yend = y2, critical = crit)\n}\n\nseg1_df <- do.call(rbind, seg1_list)\nseg2_df <- do.call(rbind, seg2_list)\nseg3_df <- do.call(rbind, seg3_list)\n\nseg1_crit  <- seg1_df[seg1_df$critical, ]\nseg1_ncrit <- seg1_df[!seg1_df$critical, ]\nseg2_crit  <- seg2_df[seg2_df$critical, ]\nseg2_ncrit <- seg2_df[!seg2_df$critical, ]\nseg3_crit  <- seg3_df[seg3_df$critical, ]\nseg3_ncrit <- seg3_df[!seg3_df$critical, ]\n\n# --- Plot ---\np <- ggplot() +\n\n  # Phase summary bars (wide, transparent — show aggregate phase duration)\n  geom_rect(\n    data = phases,\n    aes(xmin = start, xmax = end,\n        ymin = y_pos - 0.26, ymax = y_pos + 0.26,\n        fill = phase),\n    alpha = 0.3\n  ) +\n\n  # Individual task bars\n  geom_rect(\n    data = tasks,\n    aes(xmin = start, xmax = end,\n        ymin = y_pos - 0.38, ymax = y_pos + 0.38,\n        fill = phase),\n    alpha = 0.88\n  ) +\n\n  # Non-critical dependency connectors (subdued)\n  geom_segment(\n    data = seg1_ncrit,\n    aes(x = x, xend = xend, y = y, yend = yend),\n    color = INK_SOFT, linewidth = 0.45\n  ) +\n  geom_segment(\n    data = seg2_ncrit,\n    aes(x = x, xend = xend, y = y, yend = yend),\n    color = INK_SOFT, linewidth = 0.45\n  ) +\n  geom_segment(\n    data = seg3_ncrit,\n    aes(x = x, xend = xend, y = y, yend = yend),\n    color = INK_SOFT, linewidth = 0.45,\n    arrow = arrow(length = unit(0.12, \"cm\"), type = \"closed\")\n  ) +\n\n  # Critical-path dependency connectors (emphasized)\n  geom_segment(\n    data = seg1_crit,\n    aes(x = x, xend = xend, y = y, yend = yend),\n    color = INK, linewidth = 0.65\n  ) +\n  geom_segment(\n    data = seg2_crit,\n    aes(x = x, xend = xend, y = y, yend = yend),\n    color = INK, linewidth = 0.65\n  ) +\n  geom_segment(\n    data = seg3_crit,\n    aes(x = x, xend = xend, y = y, yend = yend),\n    color = INK, linewidth = 0.65,\n    arrow = arrow(length = unit(0.14, \"cm\"), type = \"closed\")\n  ) +\n\n  scale_fill_manual(values = PHASE_COLORS, name = \"Phase\") +\n  scale_x_date(\n    date_breaks = \"1 month\",\n    labels      = scales::label_date_short(),\n    expand      = expansion(mult = c(0.01, 0.02))\n  ) +\n  scale_y_continuous(\n    breaks = all_y_pos,\n    labels = all_y_lab,\n    expand = expansion(add = c(0.6, 0.6))\n  ) +\n  labs(\n    title    = \"gantt-dependencies · r · ggplot2 · anyplot.ai\",\n    subtitle = \"Arrows: finish-to-start dependencies; bold = critical path\",\n    x        = NULL,\n    y        = NULL\n  ) +\n  theme_minimal(base_size = 8) +\n  theme(\n    plot.background    = element_rect(fill = PAGE_BG, color = PAGE_BG),\n    panel.background   = element_rect(fill = PAGE_BG, color = NA),\n    panel.grid.major.x = element_line(color = INK_SOFT, linewidth = 0.18,\n                                      linetype = \"dotted\"),\n    panel.grid.major.y = element_blank(),\n    panel.grid.minor   = element_blank(),\n    axis.text.x        = element_text(color = INK_SOFT, size = 8),\n    axis.text.y        = element_text(color = INK_SOFT, size = 8, hjust = 1),\n    axis.ticks.x       = element_line(color = INK_SOFT),\n    axis.ticks.y       = element_blank(),\n    axis.line.x        = element_line(color = INK_SOFT),\n    plot.title         = element_text(color = INK, size = 12, face = \"bold\",\n                                      margin = margin(b = 3)),\n    plot.subtitle      = element_text(color = INK_MUTED, size = 8,\n                                      margin = margin(b = 10)),\n    legend.background  = element_rect(fill = ELEVATED_BG, color = NA),\n    legend.text        = element_text(color = INK_SOFT, size = 8),\n    legend.title       = element_text(color = INK, size = 9),\n    legend.position    = \"bottom\",\n    legend.direction   = \"horizontal\",\n    legend.key.size    = unit(0.4, \"cm\"),\n    plot.margin        = margin(t = 10, r = 15, b = 5, l = 5)\n  )\n\n# --- Save ---\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"}