{"spec_id":"area-mountain-panorama","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' area-mountain-panorama: Mountain Panorama Profile with Labeled Peaks\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 89/100 | Created: 2026-06-30\n\nlibrary(ggplot2)\nlibrary(ragg)\n\nset.seed(42)\n\n# Theme tokens\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\nINK_MUTED   <- if (THEME == \"light\") \"#6B6A63\" else \"#A8A79F\"\n\n# Panorama-specific colors (atmospheric tones, not categorical palette)\nSKY_BOTTOM    <- if (THEME == \"light\") \"#C8DCEC\" else \"#12203A\"\nSKY_TOP       <- if (THEME == \"light\") \"#DDEEF8\" else \"#0A121E\"\nMOUNTAIN_FILL <- if (THEME == \"light\") \"#2C2A28\" else \"#2A2825\"\nMOUNTAIN_FG   <- if (THEME == \"light\") \"#1E1C1A\" else \"#1A1815\"\nRIDGE_COLOR   <- if (THEME == \"light\") \"#4A4540\" else \"#3E3A35\"\n\n# Peak data: Wallis panorama — 10 summits in a west-to-east sweep\npeaks_df <- data.frame(\n  name      = c(\"Weisshorn\", \"Dent Blanche\", \"Matterhorn\", \"Breithorn\",\n                \"Liskamm\", \"Dufourspitze\", \"Strahlhorn\", \"Allalinhorn\",\n                \"Alphubel\", \"Dom\"),\n  angle_deg = c(  8,  28,  47,  63,  76,  90,  98, 105, 112, 118),\n  elev_m    = c(4506, 4358, 4478, 4164, 4527, 4634, 4190, 4027, 4206, 4545),\n  stringsAsFactors = FALSE\n)\n\n# Label stagger heights — SPECIAL for focal peaks, alternating HIGH/LOW for rest\npeaks_df$label_y <- c(\n  5300,  # Weisshorn     HIGH\n  5050,  # Dent Blanche  LOW\n  5500,  # Matterhorn    SPECIAL (focal)\n  5050,  # Breithorn     LOW\n  5300,  # Liskamm       HIGH\n  5500,  # Dufourspitze  SPECIAL (highest)\n  5050,  # Strahlhorn    LOW\n  5300,  # Allalinhorn   HIGH\n  5050,  # Alphubel      LOW\n  5300   # Dom           HIGH\n)\n\n# Horizontal label positions — spread dense right cluster (Strahlhorn–Dom in 20° span)\npeaks_df$label_x <- c(\n   8,   # Weisshorn\n  28,   # Dent Blanche\n  47,   # Matterhorn\n  63,   # Breithorn\n  76,   # Liskamm\n  90,   # Dufourspitze\n  93,   # Strahlhorn (nudged left from 98°)\n 105,   # Allalinhorn\n 112,   # Alphubel\n 120    # Dom (nudged right from 118°, 4° from right edge)\n)\n\n# Derived label positions\npeaks_df$seg_y1 <- peaks_df$label_y\npeaks_df$elev_y <- peaks_df$label_y + 30\npeaks_df$name_y <- peaks_df$label_y + 30 + 80 + 30\n\n# Skyline: sum of tent functions (asymmetric flanks) + smoothed noise\nBASE_ELEV <- 2750\nangles    <- seq(0, 124, by = 0.08)\nn_pts     <- length(angles)\n\nslp_left  <- c(290, 245, 370, 205, 295, 340, 230, 215, 245, 315)\nslp_right <- c(215, 290, 260, 255, 245, 265, 250, 230, 220, 270)\n\nskyline <- rep(BASE_ELEV, n_pts)\nfor (i in seq_len(nrow(peaks_df))) {\n  pa   <- peaks_df$angle_deg[i]\n  pe   <- peaks_df$elev_m[i]\n  tent <- ifelse(angles <= pa,\n                 pe - slp_left[i]  * (pa - angles),\n                 pe - slp_right[i] * (angles - pa))\n  skyline <- pmax(skyline, pmax(tent, BASE_ELEV))\n}\n\n# Ridge jaggedness: smoothed noise + micro-roughness\nnoise_sm <- as.numeric(stats::filter(rnorm(n_pts, 0, 28), rep(1 / 7, 7), sides = 2))\nnoise_sm[is.na(noise_sm)] <- 0\nskyline <- skyline + noise_sm + rnorm(n_pts, 0, 9)\n\n# Re-anchor named summits to exact elevations\nfor (i in seq_len(nrow(peaks_df))) {\n  skyline[which.min(abs(angles - peaks_df$angle_deg[i]))] <- peaks_df$elev_m[i]\n}\n\npanorama_df <- data.frame(angle = angles, elev = skyline)\n\n# Foreground ridge: closer terrain adding visual depth\nfg_ridge <- BASE_ELEV + 80 +\n  35 * sin(angles * 0.14 + 0.4) +\n  18 * cos(angles * 0.07) +\n  rnorm(n_pts, 0, 10)\nfg_df <- data.frame(angle = angles, ridge = fg_ridge)\n\n# Sky gradient raster — top row (ymax) = SKY_TOP, bottom row (ymin) = SKY_BOTTOM\nn_sky    <- 80\nsky_grad <- colorRampPalette(c(SKY_TOP, SKY_BOTTOM))(n_sky)\nsky_mat  <- matrix(sky_grad, nrow = n_sky, ncol = 1)\n\n# Title — 67 chars → ratio = 1.0 → title_size = 12\ntitle_str  <- \"Wallis Panorama · area-mountain-panorama · r · ggplot2 · anyplot.ai\"\ntitle_n    <- nchar(title_str)\ntitle_size <- max(8, round(12 * 67 / title_n))\n\np <- ggplot() +\n  # Sky gradient background (annotation_raster: row 1 = ymax = top)\n  annotation_raster(\n    raster      = sky_mat,\n    xmin        = 0,\n    xmax        = 124,\n    ymin        = BASE_ELEV,\n    ymax        = 5850,\n    interpolate = TRUE\n  ) +\n  # Main mountain silhouette\n  geom_ribbon(\n    data  = panorama_df,\n    aes(x = angle, ymin = BASE_ELEV, ymax = elev),\n    fill  = MOUNTAIN_FILL,\n    color = NA\n  ) +\n  # Foreground ridge (depth layer)\n  geom_ribbon(\n    data  = fg_df,\n    aes(x = angle, ymin = BASE_ELEV, ymax = ridge),\n    fill  = MOUNTAIN_FG,\n    color = NA\n  ) +\n  # Ridgeline edge\n  geom_line(\n    data      = panorama_df,\n    aes(x = angle, y = elev),\n    color     = RIDGE_COLOR,\n    linewidth = 0.35\n  ) +\n  # Leader lines: summit tip → label base (angled to spread-label positions)\n  geom_segment(\n    data = peaks_df,\n    aes(x = angle_deg, xend = label_x, y = elev_m + 20, yend = seg_y1),\n    color     = INK_MUTED,\n    linewidth = 0.4\n  ) +\n  # Elevation labels\n  geom_text(\n    data     = peaks_df,\n    aes(x = label_x, y = elev_y, label = paste0(elev_m, \" m\")),\n    color    = INK_SOFT,\n    size     = 2.8,\n    hjust    = 0.5,\n    vjust    = 0\n  ) +\n  # Peak names (bold, above elevation)\n  geom_text(\n    data     = peaks_df,\n    aes(x = label_x, y = name_y, label = name),\n    color    = INK,\n    size     = 3.0,\n    hjust    = 0.5,\n    vjust    = 0,\n    fontface = \"bold\"\n  ) +\n  scale_x_continuous(limits = c(0, 124), expand = expansion(0)) +\n  scale_y_continuous(\n    limits = c(BASE_ELEV, 5850),\n    breaks = c(3000, 3500, 4000, 4500, 5000),\n    labels = function(x) paste0(x, \" m\"),\n    expand = expansion(0)\n  ) +\n  labs(title = title_str, x = NULL, y = \"Elevation\") +\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 = NA,      color = NA),\n    panel.grid.major.y = element_line(color = INK_MUTED, linewidth = 0.15,\n                                      linetype = \"dashed\"),\n    panel.grid.major.x = element_blank(),\n    panel.grid.minor   = element_blank(),\n    panel.border       = element_blank(),\n    axis.title.x       = element_blank(),\n    axis.text.x        = element_blank(),\n    axis.ticks.x       = element_blank(),\n    axis.title.y       = element_text(color = INK_SOFT, size = 9),\n    axis.text.y        = element_text(color = INK_SOFT, size = 8),\n    axis.ticks.y       = element_blank(),\n    plot.title         = element_text(color = INK, size = title_size,\n                                      face = \"bold\",\n                                      margin = margin(b = 8)),\n    plot.margin        = margin(16, 16, 12, 16, unit = \"pt\")\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"}