diff --git a/plots/area-mountain-panorama/implementations/javascript/echarts.js b/plots/area-mountain-panorama/implementations/javascript/echarts.js new file mode 100644 index 0000000000..9ce6e5e91b --- /dev/null +++ b/plots/area-mountain-panorama/implementations/javascript/echarts.js @@ -0,0 +1,235 @@ +// anyplot.ai +// area-mountain-panorama: Mountain Panorama Profile with Labeled Peaks +// Library: echarts 5.5.1 | JavaScript 22.23.0 +// Quality: 91/100 | Created: 2026-06-30 + +const t = window.ANYPLOT_TOKENS; +const THEME = window.ANYPLOT_THEME || 'light'; + +// Wallis (Valais) panorama from Zermatt — Weisshorn to Rimpfischhorn +// peaks: [bearing_deg, elevation_m, name] +const peaks = [ + [6, 4506, "Weisshorn"], + [16, 4221, "Zinalrothorn"], + [24, 4063, "Ober Gabelhorn"], + [31, 4358, "Dent Blanche"], + [43, 4478, "Matterhorn"], + [56, 4164, "Breithorn"], + [63, 4092, "Pollux"], + [68, 4223, "Castor"], + [76, 4527, "Liskamm"], + [86, 4634, "Dufourspitze"], + [93, 4190, "Strahlhorn"], + [98, 4199, "Rimpfischhorn"], +]; +const MATTERHORN_IDX = 4; + +// Piecewise-linear ridgeline — sharp triangular peaks with inter-summit jaggedness +const ridgePoints = [ + [0, 3050], [1, 3150], [2, 3250], [2.5, 3420], [3.5, 3600], [4, 3800], [5, 4100], + [6, 4506], + [7, 4150], [7.5, 4000], [8, 3800], [8.8, 3650], [9.5, 3420], [10, 3480], [11, 3520], + [11.8, 3620], [12.5, 3700], [13, 3750], [13.8, 3900], [14.5, 4000], [15, 4080], [15.5, 4150], + [16, 4221], + [16.8, 4050], [17, 3900], [17.8, 3700], [18.5, 3420], [19, 3350], [19.5, 3280], + [20, 3320], [20.8, 3420], [21, 3550], [21.5, 3650], [22.5, 3820], [23, 3940], + [24, 4063], + [24.8, 3900], [25, 3720], [25.8, 3580], [26, 3300], [26.8, 3180], [27, 3080], + [27.5, 3200], [28, 3320], [28.5, 3450], [29, 3700], [29.5, 3900], [30, 4000], [30.5, 4150], + [31, 4358], + [31.5, 4200], [32, 4050], [32.8, 3800], [33.5, 3450], [34, 3280], [35, 2980], + [35.8, 2900], [36.5, 2850], [37, 2870], [37.8, 2920], [38.5, 3000], [39, 3220], [39.5, 3380], + [40, 3500], [40.8, 3650], [41.5, 3950], [42, 4150], [42.5, 4350], + [43, 4478], + [43.5, 4200], [44, 3900], [44.8, 3600], [45, 3700], [45.8, 3500], [46.5, 3150], + [47, 3080], [47.8, 2980], [48, 2950], + [48.8, 2980], [49.5, 3000], [50, 3000], [50.8, 3120], [51.5, 3200], [52, 3300], + [52.8, 3500], [53.5, 3650], [54, 3800], [54.8, 3920], [55.5, 4050], + [56, 4164], + [56.8, 4050], [57.5, 3900], [58, 3800], [58.8, 3700], [59, 3500], + [59.8, 3480], [60.5, 3480], [61, 3550], [61.8, 3650], [62, 3780], + [63, 4092], + [63.5, 3960], [64, 3850], [64.8, 3780], [65, 3750], + [65.8, 3800], [66.5, 3900], [67, 3980], [67.5, 4050], + [68, 4223], + [68.5, 4010], [69, 3850], [69.8, 3750], [70, 3650], [70.8, 3700], [71.5, 3750], + [72, 3800], [72.8, 3920], [73.5, 4050], [74, 4180], [74.8, 4300], [75, 4350], + [76, 4527], + [76.8, 4380], [77, 4280], [77.8, 4150], [78.5, 3900], [79, 3780], [79.8, 3700], + [80, 3650], [80.8, 3800], [81.5, 3950], [82, 4000], [82.8, 4150], [83.5, 4200], [84.5, 4300], + [85, 4420], [85.5, 4500], + [86, 4634], + [86.8, 4480], [87, 4380], [87.8, 4250], [88.5, 4100], [89, 3980], [89.8, 3880], + [90, 3750], [90.8, 3800], [91.5, 3850], [92, 3920], [92.5, 4000], + [93, 4190], + [93.8, 4050], [94, 3950], [94.8, 3850], [95.5, 3720], [96, 3760], [96.5, 3800], + [97, 3900], [97.5, 4030], + [98, 4199], + [98.8, 4000], [99, 3880], + [99.8, 3750], [100.5, 3600], [101, 3450], [101.8, 3300], [102.5, 3200], [103, 3100], + [103.8, 3020], [104.5, 2960], [105, 2900], +]; + +// Sky gradient — Imprint blue (#4467A3, palette[2]) layered as background series +const skyTopColor = THEME === 'dark' ? 'rgba(68,103,163,0.75)' : 'rgba(68,103,163,0.32)'; +const skyMidColor = THEME === 'dark' ? 'rgba(68,103,163,0.30)' : 'rgba(68,103,163,0.08)'; + +// Mountain silhouette — theme-adaptive ink tokens (no custom hex values) +const fillColor = t.ink; +const ridgeColor = t.inkSoft; + +// Imprint palette position 1 — highlight the focal Matterhorn peak +const BRAND = t.palette[0]; // #009E73 + +const titleText = "Alpine Panorama · area-mountain-panorama · javascript · echarts · anyplot.ai"; +const titleFontSize = Math.max(14, Math.round(22 * Math.min(1.0, 67 / titleText.length))); + +const chart = echarts.init(document.getElementById("container")); + +chart.setOption({ + animation: false, + color: t.palette, + backgroundColor: t.pageBg, + title: { + text: titleText, + left: 'center', + top: 18, + textStyle: { color: t.ink, fontSize: titleFontSize, fontWeight: 'normal' }, + }, + grid: { + left: 95, + right: 50, + top: 230, + bottom: 65, + }, + xAxis: { + type: 'value', + min: 0, + max: 105, + show: false, + }, + yAxis: { + type: 'value', + min: 2600, + max: 4900, + name: 'Elevation (m)', + nameLocation: 'middle', + nameGap: 60, + nameTextStyle: { color: t.inkSoft, fontSize: 14 }, + axisLabel: { + color: t.inkSoft, + fontSize: 13, + formatter: (v) => `${v} m`, + }, + axisLine: { show: false }, + axisTick: { show: false }, + splitLine: { lineStyle: { color: t.grid, width: 1 } }, + }, + series: [ + { + // Sky gradient — Imprint blue layered as background series behind mountain + type: 'line', + data: [[0, 4900], [105, 4900]], + smooth: false, + symbol: 'none', + lineStyle: { width: 0, opacity: 0 }, + areaStyle: { + color: { + type: 'linear', x: 0, y: 0, x2: 0, y2: 1, + colorStops: [ + { offset: 0, color: skyTopColor }, + { offset: 0.55, color: skyMidColor }, + { offset: 1, color: 'rgba(68,103,163,0)' }, + ], + }, + opacity: 1, + }, + z: -1, + }, + { + // Mountain silhouette + type: 'line', + data: ridgePoints, + smooth: false, + symbol: 'none', + lineStyle: { color: ridgeColor, width: 1.5 }, + areaStyle: { + color: fillColor, + opacity: 0.95, + }, + z: 0, + }, + ], +}); + +// Peak annotations: staggered leader lines + two-line labels (name + elevation) +// Wider xShifts for dense clusters (Pollux/Castor, Strahlhorn/Rimpfischhorn) +const yOffsets = [110, 75, 115, 80, 145, 75, 95, 60, 105, 130, 65, 92]; +const xShifts = [ 0, 0, 0, 0, 0, 0, -18, +18, 0, 0, -18, +18]; + +const graphics = []; +peaks.forEach(([angle, elev, name], i) => { + const result = chart.convertToPixel('grid', [angle, elev]); + if (!result) return; + const [px, py] = result; + + const ly = py - yOffsets[i]; + const lx = px + xShifts[i]; + const isMH = (i === MATTERHORN_IDX); + + const dotColor = isMH ? BRAND : t.inkSoft; + const lineStroke = isMH ? BRAND : t.inkSoft; + const dotR = isMH ? 5 : 3; + const nameFill = isMH ? BRAND : t.ink; + const nameFontSize = isMH ? 15 : 13; + const lineWidth = isMH ? 1.5 : 1; + + // Leader line: from just above summit dot to just below elevation label + graphics.push({ + type: 'line', + z: 20, + shape: { x1: px, y1: py - dotR - 1, x2: lx, y2: ly + 16 }, + style: { stroke: lineStroke, lineWidth, opacity: 0.7 }, + }); + + // Summit dot + graphics.push({ + type: 'circle', + z: 20, + shape: { cx: px, cy: py - dotR, r: dotR }, + style: { fill: dotColor, stroke: 'none' }, + }); + + // Peak name (bold, baseline at ly) + graphics.push({ + type: 'text', + z: 20, + x: lx, + y: ly, + style: { + text: name, + textAlign: 'center', + textBaseline: 'bottom', + fill: nameFill, + fontSize: nameFontSize, + fontWeight: 'bold', + }, + }); + + // Elevation — 14px gap below name baseline for breathing room + graphics.push({ + type: 'text', + z: 20, + x: lx, + y: ly + 14, + style: { + text: `${elev} m`, + textAlign: 'center', + textBaseline: 'top', + fill: t.inkSoft, + fontSize: 11, + }, + }); +}); + +chart.setOption({ graphic: graphics }); diff --git a/plots/area-mountain-panorama/metadata/javascript/echarts.yaml b/plots/area-mountain-panorama/metadata/javascript/echarts.yaml new file mode 100644 index 0000000000..3c516aa8a0 --- /dev/null +++ b/plots/area-mountain-panorama/metadata/javascript/echarts.yaml @@ -0,0 +1,248 @@ +library: echarts +language: javascript +specification_id: area-mountain-panorama +created: '2026-06-30T22:41:17Z' +updated: '2026-06-30T23:10:01Z' +generated_by: claude-sonnet +workflow_run: 28479242619 +issue: 5365 +language_version: 22.23.0 +library_version: 5.5.1 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/area-mountain-panorama/javascript/echarts/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/area-mountain-panorama/javascript/echarts/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/area-mountain-panorama/javascript/echarts/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/area-mountain-panorama/javascript/echarts/plot-dark.html +quality_score: 91 +review: + strengths: + - Authentic Wallis/Valais panorama data with factually correct Swiss Alps peak elevations + - Sharp piecewise-linear ridgeline correctly implements alpine silhouette (no Gaussian + bumps) + - Matterhorn focal point immediately reads via brand green (#009E73) — spec primary + design requirement fulfilled + - 'Sky gradient uses Imprint blue (palette[2] = #4467A3) keeping palette compliance + while adding atmospheric depth' + - chart.convertToPixel for dynamic leader-line positioning is the correct idiomatic + approach in ECharts + - Fully deterministic, self-contained implementation with animation:false and correct + ECharts init pattern + weaknesses: + - Peak elevation sub-labels at 11px are small; consider bumping to 12-13px for better + mobile readability when scaled to ~400px width + - Left cluster (Weisshorn, Zinalrothorn, Ober Gabelhorn) has tight horizontal spacing; + minor xShifts could improve visual breathing room + - Dark-mode silhouette inverts to white/cream (t.ink) — creative and visually compelling, + but slightly deviates from the spec dark solid color evening/dusk feel directive + image_description: |- + Light render (plot-light.png): + Background: Warm off-white (#FAF8F1) — correct theme surface + Chrome: Title "Alpine Panorama · area-mountain-panorama · javascript · echarts · anyplot.ai" clearly visible in dark ink (#1A1A17). Y-axis label "Elevation (m)" and tick labels ("2600 m", "3000 m", etc.) readable in secondary ink (#4A4A44). X-axis hidden (correct per spec). Grid lines subtle and horizontal-only. + Data: Near-black mountain silhouette (t.ink = #1A1A17) fills lower portion. Sky gradient in Imprint blue (#4467A3 at 32% opacity) above ridgeline. 12 peak annotations with leader lines, staggered heights. Matterhorn highlighted with brand green (#009E73) dot and label text — clear focal point. + Legibility verdict: PASS — all text readable against light background; elevation sub-labels at 11px are small but acceptable at canvas size. + + Dark render (plot-dark.png): + Background: Near-black (#1A1A17) — correct dark theme surface + Chrome: Title in light cream (#F0EFE8), Y-axis labels in light gray (#B8B7B0), all readable against dark background. No dark-on-dark failures. Grid lines in dark-theme grid token (rgba(240,239,232,0.15)) very subtle. + Data: Mountain silhouette appears white/cream (#F0EFE8 = t.ink in dark mode), creating snow-mountain-on-night-sky aesthetic. Sky gradient more vivid (75% opacity Imprint blue at top). Matterhorn still in brand green (#009E73) — readable on dark background. Data colors identical to light render; only chrome flips. + Legibility verdict: PASS — all text readable against dark sky background; no dark-on-dark failures observed; brand green visible on dark surface. + criteria_checklist: + visual_quality: + score: 28 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 6 + max: 8 + passed: true + comment: All font sizes explicitly set; peak names 13-15px and elevations + 11px readable at canvas size; elevation sub-labels small at mobile scale + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: Staggered yOffsets and xShifts for clustered pairs eliminate overlap + effectively + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: Mountain silhouette, summit dots, and leader lines clearly visible + in both themes + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Imprint palette with brand green focal point; CVD-safe + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Landscape orientation ideal for panoramic view; generous top margin + for annotation space; balanced margins + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Y-axis Elevation (m) with units; title descriptive; X-axis hidden + per spec allowance + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: t.pageBg background, t.palette[0] for Matterhorn, Imprint blue for + sky gradient; both renders theme-correct + design_excellence: + score: 14 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: 'Visually striking: dark silhouette vs Imprint-blue sky gradient, + Matterhorn focal point in brand green; well above generic defaults' + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: X-axis hidden, Y-axis-only subtle grid, generous annotation whitespace; + minor left-cluster spacing + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: Clear Matterhorn focal point via brand green; leader lines guide + viewer; real data provides authentic story + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct mountain panorama area chart with filled silhouette + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Piecewise-linear ridgeline, filled area, sky gradient, peak annotations + with leader lines, staggered labels, Matterhorn focal point, sensible Y-axis + lower bound + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: Bearing on X, elevation on Y; all 12 peaks correctly placed + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Alpine Panorama prefix + spec-id · javascript · echarts · anyplot.ai + format correct; no legend needed + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: Varied peak heights, steep asymmetric flanks, cols between summits, + 12 labeled peaks from Weisshorn to Rimpfischhorn + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Authentic Wallis (Valais) panorama from Zermatt with real peak names; + neutral geographic/alpine content + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: 'All elevations factually correct: Dufourspitze 4634m highest in + Switzerland, Matterhorn 4478m; Y-axis start 2600m matches Zermatt valley' + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: 'Flat structure: constants → data → init → setOption → graphics loop; + no functions or classes' + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Fully deterministic hard-coded ridge and peak data; no RNG + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: No imports; echarts is a global; no unused declarations + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean, appropriate complexity; two-pass setOption pattern is correct + ECharts idiom for graphic overlays + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: 'echarts.init/setOption without explicit size/devicePixelRatio; animation: + false; harness handles file output' + library_mastery: + score: 9 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 5 + max: 5 + passed: true + comment: 'Expert ECharts use: graphic component for annotations, z-index series + layering, linear gradient in areaStyle, animation disabled' + - id: LM-02 + name: Distinctive Features + score: 4 + max: 5 + passed: true + comment: chart.convertToPixel for data→pixel conversion is ECharts-distinctive; + two-step setOption for graphics overlay; z-layer control + verdict: APPROVED +impl_tags: + dependencies: [] + techniques: + - annotations + - html-export + patterns: + - data-generation + dataprep: [] + styling: + - alpha-blending + - gradient-fill