{"spec_id":"line-stress-strain","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' line-stress-strain: Engineering Stress-Strain Curve\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 87/100 | Created: 2026-08-24\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\"\nIMPRINT_PALETTE <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n                     \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\nBRAND <- IMPRINT_PALETTE[1]\nBAD   <- IMPRINT_PALETTE[5]  # matte red — semantic anchor for fracture/failure\n# ggplot2's element_line() has no alpha argument, so soften gridlines by\n# baking a low-alpha channel into the color itself.\nGRID_COLOR <- grDevices::adjustcolor(INK, alpha.f = 0.2)\n\n# --- Material model (aluminum alloy, uniaxial tensile test) ------------------\nelastic_modulus_mpa <- 70000  # ~70 GPa, aluminum alloy\ne_proportional       <- 0.004 # strain at the proportional limit\nstress_proportional  <- elastic_modulus_mpa * e_proportional\nasymptote_mpa        <- 310   # hardening ceiling approached near UTS\ne_uts                <- 0.09  # strain at ultimate tensile strength\nhardening_decay      <- 0.02\nfracture_stress_mpa  <- 250\ne_fracture           <- 0.16\n\nstress_uts_mpa <- asymptote_mpa -\n  (asymptote_mpa - stress_proportional) * exp(-(e_uts - e_proportional) / hardening_decay)\n\nmaterial_curve <- function(strain) {\n  ifelse(\n    strain <= e_proportional,\n    elastic_modulus_mpa * strain,\n    ifelse(\n      strain <= e_uts,\n      asymptote_mpa - (asymptote_mpa - stress_proportional) *\n        exp(-(strain - e_proportional) / hardening_decay),\n      stress_uts_mpa - (stress_uts_mpa - fracture_stress_mpa) *\n        ((strain - e_uts) / (e_fracture - e_uts))^1.5\n    )\n  )\n}\n\n# --- Sampled tensile-test data ------------------------------------------------\nn_points    <- 300\nstrain      <- seq(0, e_fracture, length.out = n_points)\nraw_noise   <- rnorm(n_points, mean = 0, sd = 1.5)\nsmooth_noise <- as.numeric(stats::filter(raw_noise, rep(1 / 4, 4), sides = 2))\nsmooth_noise[is.na(smooth_noise)] <- 0\nstress_mpa  <- material_curve(strain) + smooth_noise\ndf <- tibble::tibble(strain = strain, stress_mpa = stress_mpa)\n\n# --- Critical points: yield (0.2% offset), UTS, fracture ---------------------\noffset_root <- uniroot(\n  function(s) material_curve(s) - elastic_modulus_mpa * (s - 0.002),\n  interval = c(e_proportional, e_uts)\n)\nyield_strain <- offset_root$root\nyield_stress <- material_curve(yield_strain)\n\ncritical_points <- tibble::tibble(\n  label      = c(\"Yield (0.2% offset)\", \"UTS\", \"Fracture\"),\n  strain     = c(yield_strain, e_uts, e_fracture),\n  stress_mpa = c(yield_stress, stress_uts_mpa, fracture_stress_mpa),\n  fill_color = c(INK, INK, BAD),\n  point_size = c(3.0, 3.6, 4.2)  # size hierarchy: yield -> UTS -> fracture\n)\n\noffset_y_end <- min(340, yield_stress + 25)\noffset_x_end <- 0.002 + offset_y_end / elastic_modulus_mpa\noffset_line <- tibble::tibble(\n  strain     = c(0.002, offset_x_end),\n  stress_mpa = c(0, offset_y_end)\n)\n\n# --- Title (fontsize scales with title length, see plot-generator.md) --------\nplot_title  <- \"Aluminum Alloy Tensile Test · line-stress-strain · r · ggplot2 · anyplot.ai\"\ntitle_ratio <- if (nchar(plot_title) > 67) 67 / nchar(plot_title) else 1.0\ntitle_size  <- max(8, round(12 * title_ratio))\n\n# --- Plot ----------------------------------------------------------------------\np <- ggplot(df, aes(strain, stress_mpa)) +\n  geom_ribbon(aes(ymin = 0, ymax = stress_mpa), fill = BRAND, alpha = 0.08) +\n  geom_vline(xintercept = c(e_proportional, e_uts), linetype = \"dotted\",\n             color = INK_MUTED, linewidth = 0.4, alpha = 0.6) +\n  geom_line(data = offset_line, aes(strain, stress_mpa),\n            linetype = \"dashed\", color = INK_MUTED, linewidth = 0.7, alpha = 0.65) +\n  geom_line(color = BRAND, linewidth = 1.3) +\n  geom_point(data = critical_points, aes(strain, stress_mpa),\n             shape = 21, size = critical_points$point_size, fill = critical_points$fill_color,\n             color = PAGE_BG, stroke = 0.9) +\n  annotate(\"text\", x = e_proportional / 2, y = 335, label = \"Elastic\",\n           hjust = 0, vjust = 1, size = 3.0, color = INK_SOFT) +\n  annotate(\"text\", x = (e_proportional + e_uts) / 2, y = 335,\n           label = \"Plastic (strain hardening)\",\n           hjust = 0.5, vjust = 1, size = 3.0, color = INK_SOFT) +\n  annotate(\"text\", x = (e_uts + e_fracture) / 2, y = 335, label = \"Necking\",\n           hjust = 0.5, vjust = 1, size = 3.0, color = INK_SOFT) +\n  annotate(\"text\", x = 0.025, y = 55,\n           label = paste0(\"E ≈ \", round(elastic_modulus_mpa / 1000), \" GPa\"),\n           hjust = 0, size = 3.0, color = INK_SOFT) +\n  annotate(\"text\", x = yield_strain + 0.006, y = yield_stress - 55,\n           label = \"Yield (0.2% offset)\", hjust = 0, size = 3.0, color = INK) +\n  annotate(\"text\", x = e_uts, y = stress_uts_mpa + 20,\n           label = \"UTS\", hjust = 0.5, size = 3.0, color = INK) +\n  annotate(\"text\", x = e_fracture - 0.008, y = fracture_stress_mpa + 22,\n           label = \"Fracture\", hjust = 1, size = 3.0, color = INK) +\n  labs(\n    x     = \"Engineering Strain\",\n    y     = \"Engineering Stress (MPa)\",\n    title = plot_title\n  ) +\n  coord_cartesian(ylim = c(0, 345)) +\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.y = element_line(color = GRID_COLOR, linewidth = 0.4),\n    panel.grid.major.x = element_blank(),\n    panel.grid.minor   = element_blank(),\n    axis.title         = element_text(color = INK, size = 10),\n    axis.text          = element_text(color = INK_SOFT, size = 8),\n    axis.ticks         = element_blank(),\n    plot.title         = element_text(color = INK, size = title_size),\n    plot.margin        = margin(12, 20, 8, 8)\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"}