{"spec_id":"ternary-density","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' ternary-density: Ternary Density Plot\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 87/100 | Created: 2026-05-19\n\nlibrary(ggplot2)\nlibrary(MASS)\nlibrary(ragg)\n\nset.seed(42)\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\"\n\n# Equilateral triangle height (side = 1)\nTRI_H <- sqrt(3) / 2\n\n# Data: sediment composition (sand / silt / clay) – three geological facies\nn <- 600\n\n# Sandy facies: concentrated near sand (top) vertex\nsand1  <- rbeta(n, 8, 2)\nsplit1 <- runif(n, 0.25, 0.75)\nsilt1  <- (1 - sand1) * split1\nclay1  <- (1 - sand1) * (1 - split1)\n\n# Silty facies: concentrated near silt (bottom-left) vertex\nsilt2  <- rbeta(n, 8, 2)\nsplit2 <- runif(n, 0.25, 0.75)\nsand2  <- (1 - silt2) * split2\nclay2  <- (1 - silt2) * (1 - split2)\n\n# Clayey facies: concentrated near clay (bottom-right) vertex\nclay3  <- rbeta(n, 8, 2)\nsplit3 <- runif(n, 0.25, 0.75)\nsand3  <- (1 - clay3) * split3\nsilt3  <- (1 - clay3) * (1 - split3)\n\nsand <- c(sand1, sand2, sand3)\nsilt <- c(silt1, silt2, silt3)\nclay <- c(clay1, clay2, clay3)\n\n# Ternary -> Cartesian: Sand=top (0.5, TRI_H), Silt=bottom-left (0,0), Clay=bottom-right (1,0)\ncart_x <- 0.5 * sand + clay\ncart_y <- TRI_H * sand\n\n# KDE on Cartesian coordinates (triangle lies in [0,1] x [0, TRI_H])\nn_grid  <- 300\nkde     <- kde2d(cart_x, cart_y, n = n_grid, lims = c(0, 1, 0, TRI_H))\n\ndensity_df   <- expand.grid(x = kde$x, y = kde$y)\ndensity_df$z <- as.vector(kde$z)\n\n# Mask density values outside the equilateral triangle\ninside <- with(density_df,\n  y >= 0 &\n  x >= y / sqrt(3) &\n  x <= 1 - y / sqrt(3)\n)\ndensity_df$z[!inside] <- NA\n\n# Ternary reference grid at 20 / 40 / 60 / 80 % levels (three families of parallels)\ngrid_segs <- do.call(rbind, lapply(c(0.2, 0.4, 0.6, 0.8), function(f) {\n  # Parallel to bottom (constant sand = f)\n  yv <- f * TRI_H\n  # Parallel to left edge (constant clay = f)\n  # Parallel to right edge (constant silt = f)\n  rbind(\n    data.frame(x  = yv / sqrt(3),  xend = 1 - yv / sqrt(3), y  = yv,         yend = yv),\n    data.frame(x  = f,             xend = 0.5*(1-f)+f,       y  = 0,          yend = (1-f)*TRI_H),\n    data.frame(x  = 1-f,           xend = 0.5*(1-f),         y  = 0,          yend = (1-f)*TRI_H)\n  )\n}))\n\n# Triangle border\ntri_border <- data.frame(\n  x = c(0.5, 0, 1, 0.5),\n  y = c(TRI_H, 0, 0, TRI_H)\n)\n\n# Plot\np <- ggplot() +\n  geom_raster(\n    data = density_df,\n    aes(x = x, y = y, fill = z),\n    interpolate = TRUE\n  ) +\n  geom_contour(\n    data      = density_df,\n    aes(x = x, y = y, z = z),\n    color     = \"white\",\n    alpha     = 0.55,\n    linewidth = 0.7,\n    na.rm     = TRUE\n  ) +\n  geom_segment(\n    data      = grid_segs,\n    aes(x = x, xend = xend, y = y, yend = yend),\n    color     = INK_SOFT,\n    alpha     = 0.30,\n    linewidth = 0.4\n  ) +\n  geom_path(\n    data      = tri_border,\n    aes(x = x, y = y),\n    color     = INK,\n    linewidth = 1.4\n  ) +\n  # Vertex labels\n  annotate(\"text\", x = 0.5,   y = TRI_H + 0.09, label = \"Sand\",\n           color = INK, size = 9, fontface = \"bold\") +\n  annotate(\"text\", x = -0.09, y = -0.07,         label = \"Silt\",\n           color = INK, size = 9, fontface = \"bold\") +\n  annotate(\"text\", x = 1.09,  y = -0.07,         label = \"Clay\",\n           color = INK, size = 9, fontface = \"bold\") +\n  scale_fill_viridis_c(\n    option = \"viridis\",\n    name   = \"Density\",\n    na.value = \"transparent\",\n    guide  = guide_colorbar(barwidth = 1.8, barheight = 14,\n                            title.position = \"top\", title.hjust = 0.5)\n  ) +\n  coord_fixed(\n    xlim = c(-0.16, 1.16),\n    ylim = c(-0.16, TRI_H + 0.18)\n  ) +\n  labs(\n    title    = \"ternary-density · r · ggplot2 · anyplot.ai\",\n    subtitle = \"Sediment composition: kernel density of sand / silt / clay proportions across three geological facies\"\n  ) +\n  theme_void(base_size = 14) +\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 = 24, hjust = 0.5,\n                                     margin = margin(t = 14, b = 6)),\n    plot.subtitle     = element_text(color = INK_SOFT, size = 15, hjust = 0.5,\n                                     margin = margin(b = 10)),\n    legend.title      = element_text(color = INK,      size = 18),\n    legend.text       = element_text(color = INK_SOFT, size = 14),\n    legend.background = element_rect(fill = ELEVATED_BG, color = NA),\n    legend.position   = \"right\",\n    plot.margin       = margin(10, 80, 20, 80)\n  )\n\n# Save\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 12,\n  height   = 12,\n  units    = \"in\",\n  dpi      = 300\n)\n"}