From fbc309d30bab793c99b10cc0570383dc1a338b35 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 03:55:38 +0000 Subject: [PATCH 1/3] feat(altair): implement ridgeline-basic Regen from quality 87. Addressed: - Canvas: was 4479x2328 (undersized/overshoot vs contract); now sized view + facet spacing to land under target then PAD-only to exact 3200x1800, per the current altair.md Canvas hard rule (scale_factor 4.0, PAD-only block, no crop). - Custom non-Imprint stroke #306998 replaced with theme-adaptive INK_SOFT (no custom hexes). - Blues Vega scheme (forbidden continuous cmap) replaced with a 12-step interpolation of the Imprint imprint_seq colormap (#009E73 -> #4467A3) for the ordered month ridges. - Font sizes aligned to the scale-based family defaults (title 16px, axis label 10px, axis title 12px, header 12px) per the current Visual Sizing Defaults table. Kept: transform_density + configure_facet negative-spacing structure, seasonal temperature dataset, both-theme legibility. --- .../implementations/python/altair.py | 52 +++++++++++++------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/plots/ridgeline-basic/implementations/python/altair.py b/plots/ridgeline-basic/implementations/python/altair.py index e63c15857e..bcd5a5f94b 100644 --- a/plots/ridgeline-basic/implementations/python/altair.py +++ b/plots/ridgeline-basic/implementations/python/altair.py @@ -1,6 +1,6 @@ -""" anyplot.ai +"""anyplot.ai ridgeline-basic: Basic Ridgeline Plot -Library: altair 6.1.0 | Python 3.13.13 +Library: altair 6.2.2 | Python 3.13.13 Quality: 87/100 | Updated: 2026-04-30 """ @@ -9,6 +9,7 @@ import altair as alt import numpy as np import pandas as pd +from PIL import Image # Theme tokens @@ -18,6 +19,14 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" +# Imprint sequential colormap (brand green -> blue) for the 12 ordered ridges +IMPRINT_SEQ = ["#009E73", "#4467A3"] +_c0 = tuple(int(IMPRINT_SEQ[0][i : i + 2], 16) for i in (1, 3, 5)) +_c1 = tuple(int(IMPRINT_SEQ[1][i : i + 2], 16) for i in (1, 3, 5)) +month_colors = [ + "#{:02x}{:02x}{:02x}".format(*(round(_c0[j] + (_c1[j] - _c0[j]) * i / 11) for j in range(3))) for i in range(12) +] + # Data np.random.seed(42) months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] @@ -43,41 +52,39 @@ extent=[-15, 40], bandwidth=2, ) - .mark_area(fillOpacity=0.8, stroke="#306998", strokeWidth=2, interpolate="monotone") + .mark_area(fillOpacity=0.85, stroke=INK_SOFT, strokeWidth=1, interpolate="monotone") .encode( x=alt.X( "temperature:Q", title="Temperature (°C)", - axis=alt.Axis(labelFontSize=18, titleFontSize=22, grid=False), + axis=alt.Axis(labelFontSize=10, titleFontSize=12, grid=False), scale=alt.Scale(domain=[-15, 40]), ), y=alt.Y("density:Q", title=None, axis=None, scale=alt.Scale(domain=[0, 0.15])), - fill=alt.Fill( - "month_order:O", scale=alt.Scale(scheme="blues", domain=list(range(12)), reverse=True), legend=None - ), + fill=alt.Fill("month_order:O", scale=alt.Scale(domain=list(range(12)), range=month_colors), legend=None), row=alt.Row( "month:N", sort=months, header=alt.Header( - labelFontSize=18, labelAngle=0, labelAlign="right", labelPadding=10, title=None, labelColor=INK_SOFT + labelFontSize=12, labelAngle=0, labelAlign="right", labelPadding=8, title=None, labelColor=INK_SOFT ), ), ) .properties( - width=1400, - height=55, + width=650, + height=28, background=PAGE_BG, title=alt.Title( text="Monthly Temperature Distribution · ridgeline-basic · altair · anyplot.ai", - fontSize=28, + fontSize=16, anchor="middle", ), ) - .configure_facet(spacing=-25) + .configure_facet(spacing=-13) .configure_view(fill=PAGE_BG, stroke=None) .configure_axis( - labelFontSize=16, - titleFontSize=20, + labelFontSize=10, + titleFontSize=12, labelColor=INK_SOFT, titleColor=INK, domainColor=INK_SOFT, @@ -88,5 +95,20 @@ ) # Save -chart.save(f"plot-{THEME}.png", scale_factor=3.0) +chart.save(f"plot-{THEME}.png", scale_factor=4.0) + +# PAD-only to exact target canvas — see prompts/library/altair.md "Canvas" +TW, TH = 3200, 1800 +_img = Image.open(f"plot-{THEME}.png").convert("RGB") +_w, _h = _img.size +if _w > TW or _h > TH: + raise SystemExit( + f"altair vl-convert produced {_w}×{_h}, exceeds target {TW}×{TH}. " + f"Shrink chart .properties(width=, height=) values and re-render." + ) +if _w < TW or _h < TH: + _canvas = Image.new("RGB", (TW, TH), PAGE_BG) + _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2)) + _canvas.save(f"plot-{THEME}.png") + chart.save(f"plot-{THEME}.html") From ef7be074b9f2fe35475c5f60ee8aa0f426b05cdb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 03:55:47 +0000 Subject: [PATCH 2/3] chore(altair): add metadata for ridgeline-basic --- .../metadata/python/altair.yaml | 242 +----------------- 1 file changed, 10 insertions(+), 232 deletions(-) diff --git a/plots/ridgeline-basic/metadata/python/altair.yaml b/plots/ridgeline-basic/metadata/python/altair.yaml index e1d53d8ee0..5bfd1547bf 100644 --- a/plots/ridgeline-basic/metadata/python/altair.yaml +++ b/plots/ridgeline-basic/metadata/python/altair.yaml @@ -1,243 +1,21 @@ +# Per-library metadata for altair implementation of ridgeline-basic +# Auto-generated by impl-generate.yml + library: altair language: python specification_id: ridgeline-basic created: '2025-12-23T19:42:49Z' -updated: '2026-04-30T03:40:35Z' +updated: '2026-07-25T03:55:47Z' generated_by: claude-sonnet -workflow_run: 25145333048 +workflow_run: 30142991631 issue: 923 -python_version: 3.13.13 -library_version: 6.1.0 +language_version: 3.13.14 +library_version: 6.2.2 preview_url_light: https://storage.googleapis.com/anyplot-images/plots/ridgeline-basic/python/altair/plot-light.png preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/ridgeline-basic/python/altair/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/ridgeline-basic/python/altair/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/ridgeline-basic/python/altair/plot-dark.html -quality_score: 87 +quality_score: null review: - strengths: - - 'Fully idiomatic Altair: transform_density with groupby, configure_facet negative - spacing for ridgeline — uses native mechanisms rather than workarounds' - - Seasonal temperature dataset is realistic and well-scaled with appropriate std - deviations per month - - 'Both light and dark themes render cleanly with correct backgrounds (#FAF8F1 / - #1A1A17) and legible chrome' - - 'Strong data storytelling: blues gradient visually encodes seasonal progression, - reinforcing the narrative without a legend' - - 'Complete code quality: seed, clean imports, correct file output naming' - weaknesses: - - 'Canvas slightly undersized: width 4200px (1400x3) vs. 4800px target; increase - width to 1600' - - 'Custom stroke color #306998 is a non-Okabe-Ito hex; replace with #0072B2 (Okabe-Ito - blue) or remove explicit stroke' - - Blues sequential scheme is acceptable for 12-month ordinal data but viridis/cividis - would be more strictly compliant with the altair style guide and provide better - dark-end contrast for Jan visibility in dark mode - image_description: |- - Light render (plot-light.png): - Background: Warm off-white #FAF8F1 — correct, not pure white - Chrome: Title "Monthly Temperature Distribution · ridgeline-basic · altair · anyplot.ai" in dark ink, clearly readable. Month labels (Jan-Dec) right-aligned in soft dark ink. X-axis "Temperature (°C)" label and tick labels (-15 to 40) all clearly visible. - Data: 12 ridges using blues colormap reversed — Jan/Feb/Mar in dark navy, mid-year in medium blue, Oct/Nov/Dec in light blue/gray. Semi-transparent fills (0.8 opacity) with #306998 stroke outline. Seasonal shift clearly visible: cold months left-peaked, warm months right-peaked. - Legibility verdict: PASS — all text readable against light background, no light-on-light issues - - Dark render (plot-dark.png): - Background: Warm near-black #1A1A17 — correct, not pure black - Chrome: Title and month labels in light cream/white text — clearly readable. X-axis tick labels in light text, all legible. No dark-on-dark failures detected. - Data: Identical blues color scheme as light render. Dark navy fills (Jan/Feb) are slightly close to near-black background but distinguishable via the #306998 stroke. Light blue-gray fills (Oct/Nov/Dec) clearly visible against dark background. - Legibility verdict: PASS — all text readable against dark background, no dark-on-dark failures - criteria_checklist: - visual_quality: - score: 26 - max: 30 - items: - - id: VQ-01 - name: Text Legibility - score: 7 - max: 8 - passed: true - comment: Font sizes explicitly set (title 28px, axis 22px, ticks 18px); all - text readable in both themes - - id: VQ-02 - name: No Overlap - score: 6 - max: 6 - passed: true - comment: No problematic overlap; ridgeline overlap is intentional by design - - id: VQ-03 - name: Element Visibility - score: 5 - max: 6 - passed: true - comment: All ridges visible; Jan/Feb dark fills slightly close to dark background - but stroke provides edge contrast - - id: VQ-04 - name: Color Accessibility - score: 2 - max: 2 - passed: true - comment: Sequential blues is CVD-safe, no red-green signaling - - id: VQ-05 - name: Layout & Canvas - score: 3 - max: 4 - passed: true - comment: Width 4200px (1400x3) is below 4800px target; height appropriate - for 12-row ridgeline layout - - id: VQ-06 - name: Axis Labels & Title - score: 2 - max: 2 - passed: true - comment: Temperature (C) with units; descriptive title - - id: VQ-07 - name: Palette Compliance - score: 1 - max: 2 - passed: false - comment: 'Blues sequential acceptable for 12-category ordinal data but custom - #306998 stroke is non-Okabe-Ito; viridis/cividis would be stricter compliance' - design_excellence: - score: 12 - max: 20 - items: - - id: DE-01 - name: Aesthetic Sophistication - score: 5 - max: 8 - passed: true - comment: Reversed blues gradient encoding ordinal month order is deliberate; - negative spacing well-calibrated - - id: DE-02 - name: Visual Refinement - score: 3 - max: 6 - passed: true - comment: Right-aligned month labels, no unnecessary chrome, subtle facet dividers, - no extraneous spines - - id: DE-03 - name: Data Storytelling - score: 4 - max: 6 - passed: true - comment: 'Clear seasonal narrative: dark winter months bracket warm summer - months; temporal flow is intuitive' - spec_compliance: - score: 15 - max: 15 - items: - - id: SC-01 - name: Plot Type - score: 5 - max: 5 - passed: true - comment: Correct ridgeline plot with row-faceted density areas and partial - overlap - - id: SC-02 - name: Required Features - score: 4 - max: 4 - passed: true - comment: Stacked density curves, 12 groups, overlap via spacing=-25, chronological - ordering, color differentiation - - id: SC-03 - name: Data Mapping - score: 3 - max: 3 - passed: true - comment: X=temperature, Y=density, rows=months ordered Jan-Dec; full -15 to - 40 extent shown - - id: SC-04 - name: Title & Legend - score: 3 - max: 3 - passed: true - comment: Title includes spec-id and library in required format; no legend - needed - data_quality: - score: 15 - max: 15 - items: - - id: DQ-01 - name: Feature Coverage - score: 6 - max: 6 - passed: true - comment: 'All ridgeline aspects covered: smooth KDE curves, partial overlap, - temporal ordering, group differentiation' - - id: DQ-02 - name: Realistic Context - score: 5 - max: 5 - passed: true - comment: Monthly temperature data for temperate climate is plausible, neutral, - clearly understandable - - id: DQ-03 - name: Appropriate Scale - score: 4 - max: 4 - passed: true - comment: Base temps 2-25C with std 3-5C yields realistic seasonal distributions - code_quality: - score: 10 - max: 10 - items: - - id: CQ-01 - name: KISS Structure - score: 3 - max: 3 - passed: true - comment: No functions, no classes; flat procedural script - - id: CQ-02 - name: Reproducibility - score: 2 - max: 2 - passed: true - comment: np.random.seed(42) set - - id: CQ-03 - name: Clean Imports - score: 2 - max: 2 - passed: true - comment: Only os, altair, numpy, pandas — all used - - id: CQ-04 - name: Code Elegance - score: 2 - max: 2 - passed: true - comment: Method chaining, appropriate complexity, no fake UI - - id: CQ-05 - name: Output & API - score: 1 - max: 1 - passed: true - comment: Saves plot-{THEME}.png (scale_factor=3.0) and plot-{THEME}.html correctly - library_mastery: - score: 9 - max: 10 - items: - - id: LM-01 - name: Idiomatic Usage - score: 5 - max: 5 - passed: true - comment: transform_density with groupby, configure_facet, Header styling — - canonical Altair patterns - - id: LM-02 - name: Distinctive Features - score: 4 - max: 5 - passed: true - comment: transform_density with groupby and configure_facet(spacing=-25) are - distinctive Altair features for ridgeline - verdict: APPROVED -impl_tags: - dependencies: [] - techniques: - - faceting - - html-export - patterns: - - data-generation - dataprep: - - kde - styling: - - alpha-blending - - custom-colormap + strengths: [] + weaknesses: [] From c1d2d0661046dd4f88099394ce5d67a8c20826ad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 04:01:45 +0000 Subject: [PATCH 3/3] chore(altair): update quality score 93 and review feedback for ridgeline-basic --- .../implementations/python/altair.py | 6 +- .../metadata/python/altair.yaml | 257 +++++++++++++++++- 2 files changed, 253 insertions(+), 10 deletions(-) diff --git a/plots/ridgeline-basic/implementations/python/altair.py b/plots/ridgeline-basic/implementations/python/altair.py index bcd5a5f94b..4aac83bf79 100644 --- a/plots/ridgeline-basic/implementations/python/altair.py +++ b/plots/ridgeline-basic/implementations/python/altair.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai ridgeline-basic: Basic Ridgeline Plot -Library: altair 6.2.2 | Python 3.13.13 -Quality: 87/100 | Updated: 2026-04-30 +Library: altair 6.2.2 | Python 3.13.14 +Quality: 93/100 | Updated: 2026-07-25 """ import os diff --git a/plots/ridgeline-basic/metadata/python/altair.yaml b/plots/ridgeline-basic/metadata/python/altair.yaml index 5bfd1547bf..ae4290ac55 100644 --- a/plots/ridgeline-basic/metadata/python/altair.yaml +++ b/plots/ridgeline-basic/metadata/python/altair.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for altair implementation of ridgeline-basic -# Auto-generated by impl-generate.yml - library: altair language: python specification_id: ridgeline-basic created: '2025-12-23T19:42:49Z' -updated: '2026-07-25T03:55:47Z' +updated: '2026-07-25T04:01:44Z' generated_by: claude-sonnet workflow_run: 30142991631 issue: 923 @@ -15,7 +12,253 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/ridgeline preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/ridgeline-basic/python/altair/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/ridgeline-basic/python/altair/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/ridgeline-basic/python/altair/plot-dark.html -quality_score: null +quality_score: 93 review: - strengths: [] - weaknesses: [] + strengths: + - 'Idiomatic Altair technique: transform_density (declarative KDE) combined with + alt.Row faceting and negative configure_facet(spacing=-13) is the canonical, distinctive + way to build a ridgeline plot in this library — hard to replicate this concisely + in another library' + - 'Imprint sequential gradient (#009E73 → #4467A3) correctly applied across the + 12 chronologically-ordered ridges, with the first ridge (Jan) exactly #009E73 + as required' + - Canvas gate passes exactly at 3200×1800; verified all four edges (title, tick + labels, month row labels, x-axis title) render with generous margins and zero + clipping + - Theme-adaptive chrome (INK / INK_SOFT / PAGE_BG tokens) is threaded through axis, + row-header, title, and view configuration consistently — both light and dark renders + are fully legible with no dark-on-dark or light-on-light failures + - Realistic monthly temperature data with per-month variation in both mean (2–25°C) + and spread (std 3–5), giving each ridge a distinct silhouette rather than uniform + copies + weaknesses: + - Ridge overlap is roughly 46% (spacing=-13 vs row height=28), slightly below the + spec's 'typically 50-70%' guidance — tightening spacing further (e.g. -16 to -18) + would give a more classic joy-plot silhouette where peaks visibly poke into the + row above + - The green-to-blue color gradient is decorative/redundant with row order (no legend + or caption ties it to a meaning) — a one-line note or subtle color-mapped tick + could reinforce that it encodes month progression + - Design stays fairly minimal/flat overall; a small refinement like a subtle alternating + row background or a mean-value tick per ridge would push Aesthetic Sophistication + further above 'strong design' toward 'publication-ready' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white, matches #FAF8F1 target — not pure white. + Chrome: Title "Monthly Temperature Distribution · ridgeline-basic · altair · anyplot.ai" in bold dark ink, centered, fully visible with margin above and below. Twelve row labels (Jan-Dec) in medium-gray ink, right-aligned to the left of each ridge, all legible. X-axis "Temperature (°C)" title and tick labels (-15 to 40) in dark/soft-dark ink at the bottom, fully visible with generous margin to the canvas edge (verified via edge crops — no clipping on any side). + Data: 12 stacked, partially-overlapping density curves (KDE via transform_density), one per month, ordered chronologically top-to-bottom. Fill color runs as a smooth Imprint-sequential gradient from brand green #009E73 (Jan) to blue #4467A3 (Dec), each ridge outlined with a thin soft-ink stroke for definition. Ridge shapes vary sensibly with the seasonal cycle (coldest/left-shifted in Jan/Dec, warmest/right-shifted in Jul/Aug). + Legibility verdict: PASS — all text reads clearly against the light background, no overlaps, no edge clipping. + + Dark render (plot-dark.png): + Background: Warm near-black, matches #1A1A17 target — not pure black. + Chrome: Same title and axis text, now rendered in light ink (#F0EFE8 primary / #B8B7B0 secondary) — clearly legible against the dark surface, no dark-on-dark failures anywhere (title, row labels, tick labels, and axis title all confirmed readable). + Data: Identical 12-ridge gradient from #009E73 to #4467A3 — data colors are pixel-identical to the light render; only the chrome (background, text, baseline strokes) flipped for the dark theme, as required. + Legibility verdict: PASS — all text and data elements are clearly readable against the dark background, no dark-on-dark or light-on-light issues detected. + criteria_checklist: + visual_quality: + score: 29 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: All font sizes explicitly set (title 16, axis title 12, tick/header + labels 10-12), matches style-guide defaults, legible in both themes, no + overflow + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: No text or data collisions in either render + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: 200-point density estimate per group with fillOpacity=0.85 and a + defining stroke - clearly visible ridges + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Smooth CVD-safe green-to-blue gradient, ink stroke around each fill + for contrast, no red-green reliance + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Ridges fill the majority of the canvas with balanced margins; edge + crops confirm no cut-off content + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: '"Temperature (°C)" with units; Y-axis correctly shows group (month) + labels instead of numeric density values, per spec' + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First ridge exactly #009E73, gradient built from the canonical two-stop + imprint_seq range (#009E73 -> #4467A3); backgrounds and chrome theme-correct + in both renders' + design_excellence: + score: 15 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: Thoughtful gradient encoding of month order, clean typography, clearly + above a configured default; not yet at full publication-ready polish + - id: DE-02 + name: Visual Refinement + score: 5 + max: 6 + passed: true + comment: No grid, no distracting spines, generous whitespace between title + and ridges; per-row baseline stroke is the only minor un-refined touch + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: Row order plus color gradient jointly make the seasonal cycle immediately + visible without requiring the reader to search for the pattern + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct ridgeline / joy plot via row-faceted density curves with + overlap + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Stacked density curves, partial overlap, group labels on Y-axis, + chronological ordering, color differentiation - all present + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: X=temperature, Y=density mapped to row position; all 12 groups' full + distributions are visible + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title follows the {Descriptive Title} · {spec-id} · {library} · anyplot.ai + convention used consistently across this spec's sibling implementations; + no legend needed since row labels already identify each group + data_quality: + score: 14 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 5 + max: 6 + passed: true + comment: 12 distributions with varying mean and spread show the main seasonal + pattern; all unimodal (appropriate for temperature per month, no missed + edge case) + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: 'Neutral, comprehensible real-world scenario: monthly climate/temperature + distribution' + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: Values (2-25°C mean, 3-5°C std) are realistic for a temperate climate's + seasonal cycle + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: Linear imports -> data -> plot -> save, no functions/classes + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: np.random.seed(42) + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: os, altair, numpy, pandas, PIL.Image - all used + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Appropriately concise, no fake functionality or over-engineering + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves plot-{THEME}.png and plot-{THEME}.html via current chart.save + API + library_mastery: + score: 10 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 5 + max: 5 + passed: true + comment: Expert use of the declarative transform_density + Row-facet + negative + configure_facet(spacing=...) pattern, Altair's recommended ridgeline approach + - id: LM-02 + name: Distinctive Features + score: 5 + max: 5 + passed: true + comment: transform_density is a Vega-Lite-native declarative KDE transform + unique to Altair's grammar - other libraries need manual scipy/seaborn KDE + computation to achieve the same result + verdict: APPROVED +impl_tags: + dependencies: + - pillow + techniques: + - faceting + - html-export + patterns: + - data-generation + - iteration-over-groups + dataprep: + - kde + styling: + - alpha-blending + - gradient-fill