{"spec_id":"ternary-basic","library":"makie","language":"julia","code":"# anyplot.ai\n# ternary-basic: Basic Ternary Plot\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 91/100 | Created: 2026-08-04\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\n\n# --- Theme tokens -----------------------------------------------------------\nconst THEME    = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG  = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst INK      = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst GRID     = RGBA(INK.r, INK.g, INK.b, 0.15)\nconst BRAND    = colorant\"#009E73\"  # Imprint palette position 1\n\n# --- Data: simulated stainless-steel alloy compositions (wt%, sum to 100) --\nn_alloys     = 90\niron_pct     = 55.0 .+ 20.0 .* rand(n_alloys)\nchromium_pct = 8.0 .+ 12.0 .* rand(n_alloys)\nnickel_pct   = 100.0 .- iron_pct .- chromium_pct\n\n# --- Barycentric -> Cartesian (equilateral triangle, unit side) -------------\n# Vertices: Iron=(0,0), Chromium=(1,0), Nickel=(0.5, sqrt(3)/2)\nbary_to_xy(b, c) = Point2f(b / 100 + 0.5 * c / 100, (sqrt(3) / 2) * c / 100)\npts = bary_to_xy.(chromium_pct, nickel_pct)\n\n# Reference alloy: 18-8 (grade 304) stainless steel — Fe 74 / Cr 18 / Ni 8,\n# the textbook composition most readers will recognize as an anchor.\nref_pt = bary_to_xy(18.0, 8.0)\n\n# --- Plot ---------------------------------------------------------------\nfig = Figure(\n    resolution      = (1200, 1200),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title            = \"ternary-basic · julia · makie · anyplot.ai\",\n    titlesize        = 22,\n    titlecolor       = INK,\n    backgroundcolor  = PAGE_BG,\n    aspect           = DataAspect(),\n)\nhidedecorations!(ax)\nhidespines!(ax)\n\n# Triangle outline\nlines!(ax, Point2f[(0.0, 0.0), (1.0, 0.0), (0.5, sqrt(3) / 2), (0.0, 0.0)];\n       color = INK_SOFT, linewidth = 2.0)\n\n# Grid lines at 20% intervals — three families, each parallel to one edge,\n# batched into a single linesegments! call (Makie draws disjoint segment\n# pairs in one primitive instead of many individual lines! calls).\ngrid_pts = Point2f[]\nfor t in 0.2:0.2:0.8\n    # constant iron (parallel to the chromium-nickel edge)\n    push!(grid_pts, Point2f(1 - t, 0.0), Point2f(0.5 * (1 - t), (sqrt(3) / 2) * (1 - t)))\n    # constant chromium (parallel to the iron-nickel edge)\n    push!(grid_pts, Point2f(t, 0.0), Point2f(t + 0.5 * (1 - t), (sqrt(3) / 2) * (1 - t)))\n    # constant nickel (parallel to the iron-chromium edge)\n    push!(grid_pts, Point2f(0.5 * t, (sqrt(3) / 2) * t), Point2f(1 - 0.5 * t, (sqrt(3) / 2) * t))\nend\nlinesegments!(ax, grid_pts; color = GRID, linewidth = 1.0)\n\n# Vertex labels, offset outward from the centroid\ncentroid = (0.5, sqrt(3) / 6)\nfor (vx, vy, label) in [\n    (0.0, 0.0, \"Iron (Fe)\"),\n    (1.0, 0.0, \"Chromium (Cr)\"),\n    (0.5, sqrt(3) / 2, \"Nickel (Ni)\"),\n]\n    dx, dy = vx - centroid[1], vy - centroid[2]\n    d = sqrt(dx^2 + dy^2)\n    text!(ax, vx + 0.14 * dx / d, vy + 0.14 * dy / d;\n          text = label, color = INK, fontsize = 16, align = (:center, :center))\nend\n\n# Edge tick labels — each edge scales the component of its own vertex\nfor t in 0.2:0.2:0.8\n    text!(ax, t, -0.035; text = string(round(Int, (1 - t) * 100)),\n          color = INK_SOFT, fontsize = 11, align = (:center, :top))\n    text!(ax, 0.5 * t - 0.03, (sqrt(3) / 2) * t; text = string(round(Int, t * 100)),\n          color = INK_SOFT, fontsize = 11, align = (:right, :center))\n    text!(ax, 1 - 0.5 * t + 0.03, (sqrt(3) / 2) * t; text = string(round(Int, (1 - t) * 100)),\n          color = INK_SOFT, fontsize = 11, align = (:left, :center))\nend\n\n# Data points — single series, Imprint brand green; fill alpha < 1 keeps\n# overlapping points distinguishable in the densest part of the cluster.\nscatter!(ax, pts; color = (BRAND, 0.85), markersize = 12, strokewidth = 1.0, strokecolor = PAGE_BG)\n\n# Reference composition anchor — open diamond in the neutral ink tone marks\n# 18-8 (304) stainless, giving the reader a recognizable focal point to\n# compare the alloy cloud against. Leader runs into the empty mid-triangle\n# gap (chromium/nickel can't jointly reach x≈0.5 within the sampled ranges)\n# so the label never collides with the cluster. Drawn last (on top of the\n# grid/data) with a heavier linewidth and a tinted (not pure-background)\n# fill so the anchor never blends into surrounding elements.\nlines!(ax, [ref_pt, ref_pt + Point2f(0.28, 0.0)]; color = INK_SOFT, linewidth = 1.6)\nscatter!(ax, [ref_pt]; marker = :diamond, markersize = 26, color = (INK_SOFT, 0.12),\n         strokewidth = 2.5, strokecolor = INK)\ntext!(ax, ref_pt + Point2f(0.30, 0.0); text = \"304 (18-8 ref.)\",\n      color = INK_SOFT, fontsize = 14, align = (:left, :center))\n\nxlims!(ax, -0.18, 1.18)\nylims!(ax, -0.12, 1.06)\n\n# --- Save --------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}