diff --git a/plots/spectrum-basic/implementations/python/plotly.py b/plots/spectrum-basic/implementations/python/plotly.py index 641258d784..ac16641446 100644 --- a/plots/spectrum-basic/implementations/python/plotly.py +++ b/plots/spectrum-basic/implementations/python/plotly.py @@ -1,13 +1,24 @@ -""" pyplots.ai +""" anyplot.ai spectrum-basic: Frequency Spectrum Plot -Library: plotly 6.5.0 | Python 3.13.11 -Quality: 93/100 | Created: 2025-12-31 +Library: plotly 6.7.0 | Python 3.13.13 +Quality: 93/100 | Updated: 2026-05-14 """ +import os + import numpy as np import plotly.graph_objects as go +# Theme tokens +THEME = os.getenv("ANYPLOT_THEME", "light") +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" +GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)" +BRAND = "#009E73" + # Data - Generate synthetic signal with multiple frequency components np.random.seed(42) @@ -28,7 +39,7 @@ # Compute FFT fft_result = np.fft.rfft(signal) frequency = np.fft.rfftfreq(n_samples, 1 / sample_rate) -amplitude_db = 20 * np.log10(np.abs(fft_result) / n_samples + 1e-10) # Convert to dB +amplitude_db = 20 * np.log10(np.abs(fft_result) / n_samples + 1e-10) # Plot fig = go.Figure() @@ -38,32 +49,40 @@ x=frequency, y=amplitude_db, mode="lines", - line=dict(color="#306998", width=2), + line=dict(color=BRAND, width=3), fill="tozeroy", - fillcolor="rgba(48, 105, 152, 0.3)", + fillcolor="rgba(0, 158, 115, 0.15)", name="Spectrum", + hovertemplate="Frequency: %{x:.1f} Hz
Amplitude: %{y:.1f} dB", ) ) -# Layout +# Layout with theme-adaptive colors fig.update_layout( - title=dict(text="spectrum-basic · plotly · pyplots.ai", font=dict(size=28), x=0.5, xanchor="center"), + title=dict(text="spectrum-basic · plotly · anyplot.ai", font=dict(size=28, color=INK), x=0.5, xanchor="center"), xaxis=dict( - title=dict(text="Frequency (Hz)", font=dict(size=22)), - tickfont=dict(size=18), + title=dict(text="Frequency (Hz)", font=dict(size=22, color=INK)), + tickfont=dict(size=18, color=INK_SOFT), range=[0, 500], - gridcolor="rgba(128, 128, 128, 0.3)", - gridwidth=1, + gridcolor=GRID, + gridwidth=0.8, + linecolor=INK_SOFT, + zerolinecolor=INK_SOFT, ), yaxis=dict( - title=dict(text="Amplitude (dB)", font=dict(size=22)), - tickfont=dict(size=18), - gridcolor="rgba(128, 128, 128, 0.3)", - gridwidth=1, + title=dict(text="Amplitude (dB)", font=dict(size=22, color=INK)), + tickfont=dict(size=18, color=INK_SOFT), + gridcolor=GRID, + gridwidth=0.8, + linecolor=INK_SOFT, + zerolinecolor=INK_SOFT, ), - template="plotly_white", + paper_bgcolor=PAGE_BG, + plot_bgcolor=PAGE_BG, + font=dict(color=INK), showlegend=False, - margin=dict(l=80, r=40, t=80, b=60), + hovermode="x unified", + margin=dict(l=100, r=60, t=100, b=80), ) # Add annotations for peak frequencies @@ -76,14 +95,14 @@ text=f"{freq} Hz", showarrow=True, arrowhead=2, - arrowsize=1, + arrowsize=1.5, arrowwidth=2, - arrowcolor="#FFD43B", - font=dict(size=16, color="#306998"), + arrowcolor=INK_SOFT, + font=dict(size=16, color=INK), ax=0, - ay=-40, + ay=-50, ) # Save -fig.write_image("plot.png", width=1600, height=900, scale=3) -fig.write_html("plot.html") +fig.write_image(f"plot-{THEME}.png", width=1600, height=900, scale=3) +fig.write_html(f"plot-{THEME}.html", include_plotlyjs="cdn") diff --git a/plots/spectrum-basic/metadata/python/plotly.yaml b/plots/spectrum-basic/metadata/python/plotly.yaml index 31433971c3..568135c44e 100644 --- a/plots/spectrum-basic/metadata/python/plotly.yaml +++ b/plots/spectrum-basic/metadata/python/plotly.yaml @@ -1,164 +1,182 @@ library: plotly +language: python specification_id: spectrum-basic created: '2025-12-31T05:35:05Z' -updated: '2025-12-31T05:47:43Z' -generated_by: claude-opus-4-5-20251101 -workflow_run: 20612812571 +updated: '2026-05-14T09:17:26Z' +generated_by: claude-haiku +workflow_run: 25851877319 issue: 2926 -python_version: 3.13.11 -library_version: 6.5.0 -preview_url: https://storage.googleapis.com/anyplot-images/plots/spectrum-basic/plotly/plot.png -preview_html: https://storage.googleapis.com/anyplot-images/plots/spectrum-basic/plotly/plot.html +python_version: 3.13.13 +library_version: 6.7.0 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/spectrum-basic/python/plotly/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/spectrum-basic/python/plotly/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/spectrum-basic/python/plotly/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/spectrum-basic/python/plotly/plot-dark.html quality_score: 93 -impl_tags: - dependencies: [] - techniques: - - annotations - - fill-under-curve - - html-export - patterns: - - data-generation - dataprep: [] - styling: [] review: strengths: - - Excellent peak annotation with clear yellow arrows pointing to dominant frequencies - - Clean dB scale calculation with proper normalization and epsilon for log safety - - Realistic synthetic signal with three distinct frequency components plus noise - - Proper use of FFT with rfft/rfftfreq for real-valued signals - - Well-proportioned fill area under the curve enhances visual clarity + - Excellent visual quality with perfect text legibility in both light and dark themes + - Flawless palette compliance using Okabe-Ito brand green with correct theme-adaptive + chrome + - Professional data visualization with clear storytelling through peak frequency + annotations + - Perfect spec compliance with realistic signal processing example data + - Expert use of Plotly-specific features including area fills and annotation arrows + - Clean, reproducible code with clear structure and explicit font sizing weaknesses: - - Does not leverage Plotly's interactive features (hover tooltips showing exact - dB values, zoom capabilities display) - - Grid transparency could be more subtle (currently visible but not obtrusive) - image_description: The plot displays a frequency spectrum showing amplitude (dB) - on the Y-axis (ranging from -80 to 0) versus frequency (Hz) on the X-axis (ranging - from 0 to 500). The spectrum is rendered as a blue line (#306998) with a light - blue fill underneath. Three prominent peaks are clearly visible at 50 Hz, 120 - Hz, and 300 Hz, each annotated with yellow arrow pointers and labeled with their - respective frequencies. The 50 Hz peak is the strongest at approximately -3 dB, - the 120 Hz peak reaches about -10 dB, and the 300 Hz peak is around -15 dB. A - noisy baseline fluctuates between -45 and -70 dB. The title "spectrum-basic · - plotly · pyplots.ai" is centered at the top. The background is white with subtle - gray grid lines. + - 'DE-01: Design sophistication relies on well-configured defaults rather than distinctive + custom aesthetic choices' + - 'DE-03: Data storytelling is clear but straightforward - could emphasize the frequency + components more dramatically through visual hierarchy' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white (#FAF8F1) surface providing excellent contrast + Chrome: Title, axis labels ("Frequency (Hz)", "Amplitude (dB)"), and tick labels all clearly visible in dark text (#1A1A17) against light background. Font sizes explicitly set (title 28px, labels 22px, ticks 18px). + Data: Spectrum line rendered in brand green (#009E73) with semi-transparent fill beneath the curve (alpha~0.15). Three peak frequency annotations (50 Hz, 120 Hz, 300 Hz) with arrows clearly visible and readable. Grid lines subtle but visible. + Legibility verdict: PASS - All elements perfectly readable in light theme. + + Dark render (plot-dark.png): + Background: Warm near-black (#1A1A17) surface providing excellent contrast + Chrome: Title, axis labels, and tick labels all clearly visible in light text (#F0EFE8) against dark background. Font sizes identical to light render (28px, 22px, 18px respectively). Grid lines subtle but visible. + Data: Spectrum line remains identical green (#009E73) as in light render (confirming data colors don't flip). Semi-transparent fill has same appearance. Peak frequency annotations still clearly visible and readable. No "dark-on-dark" issues detected. + Legibility verdict: PASS - All elements perfectly readable in dark theme. Data colors identical between light and dark renders. criteria_checklist: visual_quality: - score: 38 - max: 40 + score: 30 + max: 30 items: - id: VQ-01 name: Text Legibility - score: 10 - max: 10 + score: 8 + max: 8 passed: true - comment: Title, axis labels, and tick marks are all clearly readable at appropriate - sizes + comment: All font sizes explicitly set (title 28px, labels 22px, ticks 18px). + Perfectly readable in both light and dark themes. - id: VQ-02 name: No Overlap - score: 8 - max: 8 + score: 6 + max: 6 passed: true - comment: No overlapping text; annotations are well-positioned above peaks + comment: No overlapping elements. All text fully readable and well-spaced. - id: VQ-03 name: Element Visibility - score: 8 - max: 8 + score: 6 + max: 6 passed: true - comment: Line width is appropriate for the frequency resolution; peaks are - clearly distinguishable from noise floor + comment: Spectrum line and fill optimally visible. Peak annotations clearly + marked with arrows. Data density appropriate. - id: VQ-04 name: Color Accessibility - score: 5 - max: 5 + score: 2 + max: 2 passed: true - comment: Blue color scheme with yellow annotations provides good contrast - and is colorblind-safe + comment: Okabe-Ito green (#009E73) provides excellent contrast in both themes. + Colorblind-safe. - id: VQ-05 - name: Layout Balance - score: 5 - max: 5 + name: Layout & Canvas + score: 4 + max: 4 passed: true - comment: Plot fills the canvas well with balanced margins + comment: Plot fills 70-80% of canvas with balanced margins. Excellent whitespace + utilization. - id: VQ-06 - name: Axis Labels + name: Axis Labels & Title score: 2 max: 2 passed: true - comment: 'Both axes have descriptive labels with units: "Frequency (Hz)" and - "Amplitude (dB)"' + comment: 'Descriptive labels with units: ''Frequency (Hz)'', ''Amplitude (dB)''. + Correct title format.' - id: VQ-07 - name: Grid & Legend - score: 0 + name: Palette Compliance + score: 2 max: 2 + passed: true + comment: 'First series #009E73 brand green. Backgrounds #FAF8F1 (light) and + #1A1A17 (dark). Theme-adaptive chrome perfect. Data colors identical between + renders.' + design_excellence: + score: 13 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: false + comment: Professional design with intentional color choices and clean layout. + Uses well-configured defaults with brand green and theme tokens effectively, + but more library-default than distinctively custom. + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: false + comment: Subtle grid styling and good whitespace balance. Relies primarily + on Plotly's built-in refinement rather than custom visual tweaks. + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 passed: false - comment: Grid is present but alpha is slightly high; no legend needed for - single trace + comment: Clear visual hierarchy through peak annotations and arrows highlighting + dominant frequencies. Story is readable but relatively straightforward—no + additional visual emphasis techniques. spec_compliance: - score: 25 - max: 25 + score: 15 + max: 15 items: - id: SC-01 name: Plot Type - score: 8 - max: 8 - passed: true - comment: Correct frequency spectrum line plot - - id: SC-02 - name: Data Mapping score: 5 max: 5 passed: true - comment: Frequency correctly on X-axis, amplitude (dB) on Y-axis - - id: SC-03 + comment: Correct frequency spectrum plot using line chart with filled area. + - id: SC-02 name: Required Features - score: 5 - max: 5 + score: 4 + max: 4 passed: true - comment: FFT computation, dB scale, peak annotations as suggested in spec - notes - - id: SC-04 - name: Data Range + comment: 'All spec features present: frequency axis, amplitude axis, spectrum + line, peak frequency annotations.' + - id: SC-03 + name: Data Mapping score: 3 max: 3 passed: true - comment: X-axis limited to 0-500 Hz showing the meaningful frequency range - - id: SC-05 - name: Legend Accuracy - score: 2 - max: 2 - passed: true - comment: Single trace, no legend needed; trace named appropriately - - id: SC-06 - name: Title Format - score: 2 - max: 2 + comment: X-axis (0-500 Hz) and Y-axis (dB) correctly mapped. All 256 frequency + bins displayed. + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 passed: true - comment: 'Correct format: "spectrum-basic · plotly · pyplots.ai"' + comment: Title 'spectrum-basic · plotly · anyplot.ai' correctly formatted. + Legend appropriately disabled for single series. data_quality: - score: 20 - max: 20 + score: 15 + max: 15 items: - id: DQ-01 name: Feature Coverage - score: 8 - max: 8 + score: 6 + max: 6 passed: true - comment: Shows multiple frequency components at different amplitudes, plus - realistic noise floor + comment: 'Shows all spectrum features: dominant peaks (50, 120, 300 Hz), noise + floor, complete frequency coverage.' - id: DQ-02 name: Realistic Context - score: 7 - max: 7 + score: 5 + max: 5 passed: true - comment: Signal processing scenario with plausible 50/120/300 Hz components - (power line frequency, harmonics) + comment: 'Real signal processing scenario with realistic parameters: 1000 + Hz sample rate, 1-second window. Neutral scientific context.' - id: DQ-03 name: Appropriate Scale - score: 5 - max: 5 + score: 4 + max: 4 passed: true - comment: Frequency range (0-500 Hz) and dB values (-80 to 0) are realistic - for audio/electrical signals + comment: 'Factually correct: FFT of synthetic signal with specified components. + Amplitude scale (dB) and frequency values accurate.' code_quality: score: 10 max: 10 @@ -168,42 +186,60 @@ review: score: 3 max: 3 passed: true - comment: 'Clean linear flow: imports → data generation → FFT → plot → save' + comment: 'Linear flow: imports → signal generation → FFT → plot → save. No + unnecessary functions or classes.' - id: CQ-02 name: Reproducibility - score: 3 - max: 3 + score: 2 + max: 2 passed: true - comment: Uses np.random.seed(42) + comment: Uses np.random.seed(42) for reproducible noise generation. - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: Only numpy and plotly.graph_objects imported, both used + comment: 'All imports necessary: os (THEME), numpy (FFT), plotly (plotting). + No unused imports.' - id: CQ-04 - name: No Deprecated API - score: 1 - max: 1 + name: Code Elegance + score: 2 + max: 2 passed: true - comment: Uses current Plotly APIs + comment: Pythonic, well-structured. No over-engineering or fake functionality. - id: CQ-05 - name: Output Correct + name: Output & API score: 1 max: 1 passed: true - comment: Saves as plot.png and plot.html - library_features: - score: 0 - max: 5 + comment: 'Correct output format: plot-{THEME}.png and plot-{THEME}.html. Current + Plotly API.' + library_mastery: + score: 10 + max: 10 items: - - id: LF-01 - name: Uses distinctive library features - score: 0 + - id: LM-01 + name: Idiomatic Usage + score: 5 max: 5 - passed: false - comment: While the implementation is correct, it doesn't leverage Plotly's - interactive features in the static output; the fill area and annotations - are basic plotly features but nothing that showcases Plotly's unique strengths - like hover info customization or range sliders + passed: true + comment: 'Expert use of Plotly patterns: go.Figure(), go.Scatter(), update_layout(), + annotations. High-level API used effectively.' + - id: LM-02 + name: Distinctive Features + score: 5 + max: 5 + passed: true + comment: 'Leverages Plotly-specific features: fill=''tozeroy'' with custom + alpha, annotation arrows, hover templates, hovermode unified.' verdict: APPROVED +impl_tags: + dependencies: [] + techniques: + - annotations + - html-export + patterns: + - data-generation + dataprep: [] + styling: + - alpha-blending