{"spec_id":"line-load-duration","library":"echarts","language":"javascript","code":"// anyplot.ai\n// line-load-duration: Load Duration Curve for Energy Systems\n// Library: echarts 5.5.1 | JavaScript 22.22.3\n// Quality: 90/100 | Created: 2026-06-10\n\nconst t = window.ANYPLOT_TOKENS;\nconst isDark = window.ANYPLOT_THEME === \"dark\";\n\n// Convert hex palette entry to rgba string\nfunction hexAlpha(hex, alpha) {\n    const r = parseInt(hex.slice(1, 3), 16);\n    const g = parseInt(hex.slice(3, 5), 16);\n    const b = parseInt(hex.slice(5, 7), 16);\n    return `rgba(${r},${g},${b},${alpha})`;\n}\n\n// Region fill alpha — higher in dark mode to preserve contrast\nconst fillAlpha = isDark ? 0.20 : 0.13;\nconst peakFill  = hexAlpha(t.palette[4], fillAlpha);   // matte red #AE3030\nconst interFill = hexAlpha(t.palette[3], fillAlpha);   // ochre     #BD8233\nconst baseFill  = hexAlpha(t.palette[2], fillAlpha);   // blue      #4467A3\n\nconst peakLabel  = hexAlpha(t.palette[4], 0.85);\nconst interLabel = hexAlpha(t.palette[3], 0.85);\nconst baseLabel  = hexAlpha(t.palette[2], 0.85);\n\n// Deterministic LCG random number generator (seed 42)\nlet _s = 42;\nfunction rnd() {\n    _s = (Math.imul(_s, 1664525) + 1013904223) >>> 0;\n    return _s / 4294967295;\n}\n\n// Synthetic annual hourly load: seasonal + daily patterns + noise\nconst raw = Array.from({length: 8760}, function(_, h) {\n    const seasonal = 250 * Math.cos((Math.floor(h / 24) / 365) * 2 * Math.PI);\n    const daily    =  90 * Math.sin(((h % 24 - 3) / 24) * 2 * Math.PI);\n    return Math.max(380, 790 + seasonal + daily + (rnd() - 0.5) * 190);\n});\n\n// Sort descending → load duration curve\nraw.sort(function(a, b) { return b - a; });\nconst load = raw.map(function(v) { return Math.round(v); });\n\n// Total annual energy (MWh — one value per hour)\nconst totalMWh = load.reduce(function(s, v) { return s + v; }, 0);\nconst totalTWh = (totalMWh / 1e6).toFixed(2);\n\n// Region boundaries where load crosses generation capacity thresholds\nconst PEAK_CAP = 900;   // MW — peaking plant threshold\nconst BASE_CAP = 600;   // MW — base-load plant threshold\nconst peakEnd  = load.findIndex(function(v) { return v < PEAK_CAP; });\nconst interEnd = load.findIndex(function(v) { return v < BASE_CAP; });\n\nconst chart = echarts.init(document.getElementById(\"container\"));\n\nchart.setOption({\n    animation: false,\n    color: t.palette,\n    backgroundColor: \"transparent\",\n\n    title: {\n        text: \"line-load-duration · javascript · echarts · anyplot.ai\",\n        left: \"center\",\n        top: 22,\n        textStyle: { color: t.ink, fontSize: 22, fontWeight: \"500\" }\n    },\n\n    grid: { left: 105, right: 185, top: 88, bottom: 88 },\n\n    xAxis: {\n        type: \"value\",\n        name: \"Duration (hours per year)\",\n        nameLocation: \"middle\",\n        nameGap: 48,\n        min: 0,\n        max: 8760,\n        interval: 1000,\n        nameTextStyle: { color: t.inkSoft, fontSize: 16 },\n        axisLabel:  { color: t.inkSoft, fontSize: 13 },\n        axisLine:   { lineStyle: { color: t.inkSoft } },\n        axisTick:   { show: false },\n        splitLine:  { show: false },\n    },\n\n    yAxis: {\n        type: \"value\",\n        name: \"Load (MW)\",\n        nameLocation: \"middle\",\n        nameGap: 65,\n        min: 300,\n        max: 1350,\n        nameTextStyle: { color: t.inkSoft, fontSize: 16 },\n        axisLabel:  { color: t.inkSoft, fontSize: 13 },\n        axisLine:   { lineStyle: { color: t.inkSoft } },\n        axisTick:   { show: false },\n        splitLine:  { lineStyle: { color: t.grid, width: 1 } },\n    },\n\n    series: [{\n        type: \"line\",\n        data: load.map(function(v, i) { return [i, v]; }),\n        showSymbol: false,\n        lineStyle: { color: t.palette[0], width: 3 },\n\n        markLine: {\n            silent: true,\n            symbol: \"none\",\n            lineStyle: { type: \"dashed\", color: t.inkSoft, width: 1.5 },\n            label: {\n                show: true,\n                position: \"end\",\n                fontSize: 13,\n                color: t.inkSoft,\n                backgroundColor: t.elevatedBg,\n                padding: [3, 7],\n                borderRadius: 2,\n            },\n            data: [\n                { yAxis: PEAK_CAP, label: { formatter: \"Intermediate cap: \" + PEAK_CAP + \" MW\" } },\n                { yAxis: BASE_CAP, label: { formatter: \"Base cap: \"         + BASE_CAP + \" MW\" } },\n            ]\n        },\n\n        markArea: {\n            silent: true,\n            data: [\n                [\n                    { xAxis: 0,        itemStyle: { color: peakFill  } },\n                    { xAxis: peakEnd }\n                ],\n                [\n                    { xAxis: peakEnd,  itemStyle: { color: interFill } },\n                    { xAxis: interEnd }\n                ],\n                [\n                    { xAxis: interEnd, itemStyle: { color: baseFill  } },\n                    { xAxis: 8760 }\n                ],\n            ]\n        },\n    }],\n\n    graphic: [\n        {\n            type: \"text\",\n            left: \"9%\",\n            top: \"48%\",\n            style: { text: \"Peak Load\",    fontSize: 15, fontWeight: \"bold\", fill: peakLabel  }\n        },\n        {\n            type: \"text\",\n            left: \"38%\",\n            top: \"58%\",\n            style: { text: \"Intermediate\", fontSize: 15, fontWeight: \"bold\", fill: interLabel }\n        },\n        {\n            type: \"text\",\n            left: \"73%\",\n            top: \"73%\",\n            style: { text: \"Base Load\",    fontSize: 15, fontWeight: \"bold\", fill: baseLabel  }\n        },\n        {\n            type: \"text\",\n            left: \"56%\",\n            top: \"78%\",\n            style: { text: \"Total annual energy: \" + totalTWh + \" TWh\", fontSize: 14, fill: t.inkSoft }\n        },\n    ],\n});\n"}