diff --git a/plots/line-stress-strain/implementations/python/bokeh.py b/plots/line-stress-strain/implementations/python/bokeh.py index fa84c3cfc5..4c43adcfd1 100644 --- a/plots/line-stress-strain/implementations/python/bokeh.py +++ b/plots/line-stress-strain/implementations/python/bokeh.py @@ -1,16 +1,44 @@ -""" pyplots.ai +""" anyplot.ai line-stress-strain: Engineering Stress-Strain Curve -Library: bokeh 3.9.0 | Python 3.14.3 -Quality: 91/100 | Created: 2026-03-20 +Library: bokeh 3.9.1 | Python 3.13.14 +Quality: 89/100 | Updated: 2026-06-21 """ -import numpy as np -from bokeh.io import export_png -from bokeh.models import ColumnDataSource, HoverTool, Label, Legend, LegendItem, Span -from bokeh.plotting import figure, save +import os +import sys +import time +from pathlib import Path -# Data — Mild steel tensile test simulation +# Prevent this file (bokeh.py) from shadowing the installed bokeh package +_here = str(Path(__file__).resolve().parent) +sys.path = [p for p in sys.path if Path(p).resolve() != Path(_here)] + +import numpy as np +from bokeh.io import output_file, save +from bokeh.models import ColumnDataSource, HoverTool, Label, Legend, LegendItem, Span +from bokeh.plotting import figure +from bokeh.resources import CDN +from selenium import webdriver +from selenium.webdriver.chrome.options import Options + + +# Theme tokens — Imprint palette, theme-adaptive chrome +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" +INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" + +# Imprint palette — data element colors +COLOR_CURVE = "#009E73" # position 1 — brand green — primary stress-strain curve +COLOR_YIELD = "#4467A3" # position 3 — blue — yield point +COLOR_UTS = "#C475FD" # position 2 — lavender — ultimate tensile strength +COLOR_FRACTURE = "#AE3030" # position 5 — matte red — fracture (semantic: material failure) +COLOR_OFFSET = "#BD8233" # position 4 — ochre — 0.2% offset reference line + +# Data — Mild steel tensile test (Ludwik power law model) np.random.seed(42) youngs_modulus = 210000 # MPa @@ -24,21 +52,19 @@ strain_elastic = np.linspace(0, yield_strain, 60) stress_elastic = youngs_modulus * strain_elastic -# Yield plateau and strain hardening (Ludwik-type power law) +# Strain hardening — Ludwik power law strain_plastic = np.linspace(yield_strain, uts_strain, 200) plastic_strain = strain_plastic - yield_strain stress_plastic = yield_strength + (uts - yield_strength) * (plastic_strain / (uts_strain - yield_strain)) ** 0.45 -# Necking region (stress decreases after UTS) +# Necking region (stress drops after UTS) strain_necking = np.linspace(uts_strain, fracture_strain, 80) necking_progress = (strain_necking - uts_strain) / (fracture_strain - uts_strain) stress_necking = uts - (uts - 320) * necking_progress**0.8 -# Combine all regions +# Combined curve with region labels for hover tooltip strain = np.concatenate([strain_elastic, strain_plastic, strain_necking]) stress = np.concatenate([stress_elastic, stress_plastic, stress_necking]) - -# Region labels for hover tooltip region_labels = np.concatenate( [ np.full(len(strain_elastic), "Elastic"), @@ -47,59 +73,56 @@ ] ) -# 0.2% offset line — extended to be clearly visible -offset_strain_start = 0.002 -offset_strain_end = 0.004 + yield_strength / youngs_modulus -offset_strain_line = np.linspace(offset_strain_start, offset_strain_end, 80) +# 0.2% offset line (parallel to elastic slope, shifted right by 0.002) +offset_strain_line = np.linspace(0.002, 0.002 + yield_strength / youngs_modulus + 0.002, 80) offset_stress_line = youngs_modulus * (offset_strain_line - 0.002) mask = offset_stress_line <= yield_strength + 30 offset_strain_line = offset_strain_line[mask] offset_stress_line = offset_stress_line[mask] # Key points -yield_point_strain = yield_strain + 0.002 +yield_point_strain = yield_strain + 0.002 # intersection of offset line with curve yield_point_stress = yield_strength uts_point_strain = uts_strain uts_point_stress = uts fracture_point_strain = fracture_strain fracture_point_stress = stress_necking[-1] -# Colorblind-safe palette -color_main = "#306998" # Python blue -color_yield = "#D4A84B" # Gold -color_uts = "#8B5CF6" # Purple (replaces red) -color_fracture = "#0EA5E9" # Sky blue (replaces green) -color_region = "#6B7280" # Neutral gray - -# Plot +# Canvas — hard rule: 3200×1800 landscape +W, H = 3200, 1800 p = figure( - width=4800, - height=2700, - title="line-stress-strain · bokeh · pyplots.ai", + width=W, + height=H, + title="line-stress-strain · python · bokeh · anyplot.ai", x_axis_label="Engineering Strain (mm/mm)", y_axis_label="Engineering Stress (MPa)", + toolbar_location=None, # omit to keep PNG at exactly W×H + min_border_bottom=160, # room for 34pt tick labels + 42pt axis label + min_border_left=180, # room for 34pt tick labels + 42pt axis label + min_border_top=110, # room for 50pt title + min_border_right=60, ) -# Subtle horizontal reference lines at yield and UTS +# Subtle horizontal reference lines at yield and UTS stress levels p.add_layout( Span( location=yield_strength, dimension="width", - line_color=color_yield, - line_alpha=0.2, + line_color=COLOR_YIELD, + line_alpha=0.18, line_dash="dotted", line_width=2, ) ) p.add_layout( - Span(location=uts, dimension="width", line_color=color_uts, line_alpha=0.2, line_dash="dotted", line_width=2) + Span(location=uts, dimension="width", line_color=COLOR_UTS, line_alpha=0.18, line_dash="dotted", line_width=2) ) -# Main curve with region data for HoverTool +# Main stress-strain curve source = ColumnDataSource(data={"strain": strain, "stress": stress, "region": region_labels}) -main_line = p.line(x="strain", y="stress", source=source, line_width=5, color=color_main) +main_line = p.line(x="strain", y="stress", source=source, line_width=5, color=COLOR_CURVE) -# HoverTool — Bokeh-distinctive interactive feature +# HoverTool — Bokeh interactive region-aware tooltip hover = HoverTool( renderers=[main_line], tooltips=[("Strain", "@strain{0.0000}"), ("Stress", "@stress{0.1} MPa"), ("Region", "@region")], @@ -108,103 +131,95 @@ ) p.add_tools(hover) -# 0.2% offset line — thicker and more visible +# 0.2% offset line — dashed reference for yield point determination offset_source = ColumnDataSource(data={"strain": offset_strain_line, "stress": offset_stress_line}) -offset_line = p.line(x="strain", y="stress", source=offset_source, line_width=4, line_dash="dashed", color=color_yield) +offset_line = p.line(x="strain", y="stress", source=offset_source, line_width=3, line_dash="dashed", color=COLOR_OFFSET) -# Key points — larger markers for clarity +# Key point markers — distinct shapes for redundant encoding (5 series) yield_glyph = p.scatter( x=[yield_point_strain], y=[yield_point_stress], - size=32, - color=color_yield, + size=28, + color=COLOR_YIELD, marker="circle", - line_color="white", + line_color=PAGE_BG, line_width=3, ) - uts_glyph = p.scatter( x=[uts_point_strain], y=[uts_point_stress], - size=32, - color=color_uts, + size=28, + color=COLOR_UTS, marker="triangle", - line_color="white", + line_color=PAGE_BG, line_width=3, ) - fracture_glyph = p.scatter( x=[fracture_point_strain], y=[fracture_point_stress], - size=32, - color=color_fracture, + size=28, + color=COLOR_FRACTURE, marker="square", - line_color="white", + line_color=PAGE_BG, line_width=3, ) -# Region labels — repositioned to reduce left-side congestion +# Region labels — spread across zones to reduce left-side congestion p.add_layout( - Label(x=0.008, y=130, text="Elastic", text_font_size="22pt", text_color=color_region, text_font_style="italic") + Label(x=0.006, y=155, text="Elastic", text_font_size="34pt", text_color=INK_MUTED, text_font_style="italic") ) - p.add_layout( - Label( - x=0.08, y=280, text="Strain Hardening", text_font_size="22pt", text_color=color_region, text_font_style="italic" - ) + Label(x=0.09, y=295, text="Strain Hardening", text_font_size="34pt", text_color=INK_MUTED, text_font_style="italic") ) - p.add_layout( - Label(x=0.27, y=380, text="Necking", text_font_size="22pt", text_color=color_region, text_font_style="italic") + Label(x=0.26, y=390, text="Necking", text_font_size="34pt", text_color=INK_MUTED, text_font_style="italic") ) -# Key point annotations — repositioned to spread out and avoid left-side crowding +# Key point annotations — positioned to avoid elastic-zone crowding p.add_layout( Label( - x=yield_point_strain + 0.018, - y=yield_point_stress - 25, - text=f"Yield Point ({yield_point_stress} MPa)", - text_font_size="18pt", - text_color=color_yield, + x=yield_point_strain + 0.022, + y=yield_point_stress - 30, + text=f"Yield ({yield_point_stress} MPa)", + text_font_size="28pt", + text_color=COLOR_YIELD, text_font_style="bold", ) ) - p.add_layout( Label( x=uts_point_strain - 0.075, - y=uts_point_stress + 18, + y=uts_point_stress + 16, text=f"UTS ({uts_point_stress} MPa)", - text_font_size="18pt", - text_color=color_uts, + text_font_size="28pt", + text_color=COLOR_UTS, text_font_style="bold", ) ) - p.add_layout( Label( - x=fracture_point_strain - 0.04, + x=fracture_point_strain - 0.055, y=fracture_point_stress - 40, text="Fracture", - text_font_size="18pt", - text_color=color_fracture, + text_font_size="28pt", + text_color=COLOR_FRACTURE, text_font_style="bold", ) ) -# Young's modulus annotation — moved right to reduce elastic region crowding +# Young's modulus annotation — moved right to clear the elastic/yield zone p.add_layout( Label( - x=0.025, - y=50, + x=0.040, + y=45, text=f"E = {youngs_modulus // 1000} GPa", - text_font_size="18pt", - text_color=color_main, + text_font_size="26pt", + text_color=COLOR_CURVE, text_font_style="bold", ) ) -# Legend — positioned inside the plot near the data +# Legend — bottom-right placement, theme-adaptive styling legend = Legend( items=[ LegendItem(label="Stress-Strain Curve", renderers=[main_line]), @@ -213,55 +228,79 @@ LegendItem(label="Ultimate Tensile Strength", renderers=[uts_glyph]), LegendItem(label="Fracture Point", renderers=[fracture_glyph]), ], - location=(2800, 200), + location="bottom_right", ) -legend.label_text_font_size = "18pt" -legend.glyph_height = 30 -legend.glyph_width = 30 -legend.spacing = 10 -legend.padding = 20 +legend.label_text_font_size = "28pt" +legend.label_text_color = INK_SOFT +legend.glyph_height = 38 +legend.glyph_width = 38 +legend.spacing = 12 +legend.padding = 24 legend.margin = 20 -legend.background_fill_color = "#FAFAFA" -legend.background_fill_alpha = 0.9 -legend.border_line_color = "#E5E7EB" -legend.border_line_alpha = 0.6 -legend.border_line_width = 2 +legend.background_fill_color = ELEVATED_BG +legend.background_fill_alpha = 0.92 +legend.border_line_color = INK_SOFT +legend.border_line_alpha = 0.35 +legend.border_line_width = 1 p.add_layout(legend, "center") -# Style -p.title.text_font_size = "28pt" +# Typography — canonical Bokeh sizes for 3200×1800 +p.title.text_font_size = "50pt" p.title.text_font_style = "normal" -p.title.text_color = "#374151" -p.xaxis.axis_label_text_font_size = "22pt" -p.yaxis.axis_label_text_font_size = "22pt" -p.xaxis.major_label_text_font_size = "18pt" -p.yaxis.major_label_text_font_size = "18pt" -p.xaxis.axis_label_text_color = "#4B5563" -p.yaxis.axis_label_text_color = "#4B5563" -p.xaxis.major_label_text_color = "#6B7280" -p.yaxis.major_label_text_color = "#6B7280" -p.xaxis.axis_line_color = "#D1D5DB" -p.yaxis.axis_line_color = "#D1D5DB" -p.xaxis.major_tick_line_color = "#D1D5DB" -p.yaxis.major_tick_line_color = "#D1D5DB" +p.title.text_color = INK +p.xaxis.axis_label_text_font_size = "42pt" +p.yaxis.axis_label_text_font_size = "42pt" +p.xaxis.major_label_text_font_size = "34pt" +p.yaxis.major_label_text_font_size = "34pt" +p.xaxis.axis_label_text_color = INK +p.yaxis.axis_label_text_color = INK +p.xaxis.major_label_text_color = INK_SOFT +p.yaxis.major_label_text_color = INK_SOFT + +# Axes — L-shaped frame (outline None, axis lines kept) +p.xaxis.axis_line_color = INK_SOFT +p.yaxis.axis_line_color = INK_SOFT +p.xaxis.major_tick_line_color = INK_SOFT +p.yaxis.major_tick_line_color = INK_SOFT p.xaxis.minor_tick_line_color = None p.yaxis.minor_tick_line_color = None -p.xgrid.grid_line_alpha = 0.15 -p.ygrid.grid_line_alpha = 0.15 -p.xgrid.grid_line_color = "#9CA3AF" -p.ygrid.grid_line_color = "#9CA3AF" +# Grid — subtle, y-axis only for line chart +p.xgrid.grid_line_color = None +p.ygrid.grid_line_color = INK +p.ygrid.grid_line_alpha = 0.12 +# Background +p.background_fill_color = PAGE_BG +p.border_fill_color = PAGE_BG p.outline_line_color = None -p.toolbar_location = None -p.background_fill_color = "#FAFAFA" -p.border_fill_color = "white" +# Data ranges p.y_range.start = -10 p.y_range.end = 450 p.x_range.start = -0.005 p.x_range.end = 0.38 -# Save -export_png(p, filename="plot.png") -save(p, filename="plot.html", title="line-stress-strain · bokeh · pyplots.ai") +# Save interactive HTML (catalog artifact) +output_file(f"plot-{THEME}.html") +save(p, resources=CDN) + +# Screenshot with headless Chrome — CDP override forces exact W×H viewport +opts = Options() +for arg in ( + "--headless=new", + "--no-sandbox", + "--disable-dev-shm-usage", + "--disable-gpu", + f"--window-size={W},{H}", + "--hide-scrollbars", +): + opts.add_argument(arg) +driver = webdriver.Chrome(options=opts) +driver.execute_cdp_cmd( + "Emulation.setDeviceMetricsOverride", {"width": W, "height": H, "deviceScaleFactor": 1, "mobile": False} +) +driver.get(f"file://{Path(f'plot-{THEME}.html').resolve()}") +time.sleep(3) +driver.save_screenshot(f"plot-{THEME}.png") +driver.quit() diff --git a/plots/line-stress-strain/metadata/python/bokeh.yaml b/plots/line-stress-strain/metadata/python/bokeh.yaml index bae28eda74..43a5d108ab 100644 --- a/plots/line-stress-strain/metadata/python/bokeh.yaml +++ b/plots/line-stress-strain/metadata/python/bokeh.yaml @@ -1,116 +1,141 @@ library: bokeh +language: python specification_id: line-stress-strain created: '2026-03-20T21:23:53Z' -updated: '2026-03-20T21:47:23Z' -generated_by: claude-opus-4-5-20251101 -workflow_run: 23363005492 +updated: '2026-06-21T09:45:23Z' +generated_by: claude-sonnet +workflow_run: 27899990977 issue: 4413 -python_version: 3.14.3 -library_version: 3.9.0 -preview_url: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/bokeh/plot.png -preview_html: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/bokeh/plot.html -quality_score: 91 +language_version: 3.13.14 +library_version: 3.9.1 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/python/bokeh/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/python/bokeh/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/python/bokeh/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/python/bokeh/plot-dark.html +quality_score: 89 review: strengths: - - Excellent spec compliance — all required features (region labels, critical points, - offset line, Young's modulus annotation) are present and well-positioned - - Realistic engineering data with physically accurate material model (Ludwik power - law, proper necking behavior) - - Professional visual polish with custom color palette, subtle grid, and styled - typography - - Effective use of Bokeh-specific features (HoverTool, Span, dual PNG+HTML export) + - 'Perfect data quality: factually correct mild steel parameters (E=210 GPa, yield=250 + MPa, UTS=400 MPa) with a physically accurate Ludwik power law model' + - 'Excellent Imprint palette compliance: semantic matte red (#AE3030) for fracture, + CVD-safe shape redundancy (circle/triangle/square), correct palette order' + - 'Expert Bokeh idioms: ColumnDataSource with @region column for hover, HoverTool + in vline mode, CDN HTML export, Span cross-lines, LegendItem manual construction' + - Two-level annotation hierarchy (italic+muted for region labels, bold+palette for + key-point labels) creates clean visual grammar + - Full theme-adaptive chrome with correct tokens in both renders — no dark-on-dark + failures in dark render weaknesses: - - Left side of the plot remains slightly congested with the elastic region, yield - marker, offset line, and multiple labels compressed into a narrow horizontal space - - Legend could be more prominently positioned or styled to better match the overall - design quality - image_description: 'The plot displays an engineering stress-strain curve for mild - steel on a light gray (#FAFAFA) background. A blue (#306998) line traces the material - response from zero through a steep elastic region, a gradual strain-hardening - curve peaking at ~400 MPa around strain 0.22, and a declining necking region ending - at a fracture point near strain 0.35. Three region labels in gray italic text - ("Elastic", "Strain Hardening", "Necking") are positioned within their respective - zones. Critical points are marked with distinct shapes: a gold circle for Yield - Point (250 MPa), a purple triangle for UTS (400 MPa), and a sky-blue square for - Fracture. A dashed gold 0.2% offset line is visible near the elastic region. "E - = 210 GPa" is annotated in blue. Subtle dotted horizontal reference lines appear - at yield and UTS stress levels. The title reads "line-stress-strain · bokeh · - pyplots.ai" in the upper left. A styled legend box in the lower-center area lists - all five elements. Axes are labeled "Engineering Strain (mm/mm)" and "Engineering - Stress (MPa)" with gray-toned tick labels. Grid lines are very subtle (alpha 0.15), - and the plot outline is removed.' + - '0.2% offset line is nearly invisible: x-axis spans 0–0.38 but offset line only + runs from x=0.002 to ~0.005, appearing as ~3 tiny dashes next to y-axis. The spec + core goal of illustrating yield point determination method is not achieved. Fix: + extend offset line to x=0.06 (keeping it below yield_strength so it does not cross + the curve) to make slope and 0.002-strain offset clearly visible; or add a zoomed + inset of the elastic-plastic transition region.' + - 'Left-side annotation congestion: elastic label, yield point annotation, E=210 + GPa annotation, and compressed offset line all compete in <3% of the x-axis, creating + visual clutter near the elastic region. Pulling E=210 GPa and Yield annotation + slightly further right would reduce this.' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white #FAF8F1 — correct theme surface, not pure white + Chrome: Title "line-stress-strain · python · bokeh · anyplot.ai" in dark INK (#1A1A17), readable. Axis labels "Engineering Strain (mm/mm)" and "Engineering Stress (MPa)" in dark INK, clear with units. Tick labels in INK_SOFT (#4A4A44), readable. Region labels "Elastic", "Strain Hardening", "Necking" in italic INK_MUTED (#6B6A63), visible. Point annotations "Yield (250 MPa)", "UTS (400 MPa)", "Fracture", "E = 210 GPa" in respective palette colors, all readable. Legend bottom-right with ELEVATED_BG (#FFFDF6) semi-transparent fill, INK_SOFT labels. + Data: Primary stress-strain curve in #009E73 (brand green), line_width=5, prominent. Yield point blue circle (#4467A3) at x≈0.003, y=250. UTS lavender triangle (#C475FD) at x=0.22, y=400. Fracture red square (#AE3030) at x=0.35, y≈320. Ochre dashed offset line (#BD8233) barely visible near y-axis (x=0.002 to ~0.005, ~1% of plot width). Subtle horizontal dotted reference spans at y=250 and y=400. + Legibility verdict: PASS — all text readable against light background. No legibility failures. + + Dark render (plot-dark.png): + Background: Warm near-black #1A1A17 — correct dark theme surface, not pure black + Chrome: Title in light INK (#F0EFE8), clearly readable. Axis labels in light INK (#F0EFE8), readable. Tick labels in INK_SOFT (#B8B7B0), readable. Region italic labels in INK_MUTED (#A8A79F), visible against dark background. Point annotations in their respective palette colors (blue, lavender, red, green), all visible on dark surface. Legend uses ELEVATED_BG (#242420) with INK_SOFT (#B8B7B0) label text — readable. No dark-on-dark failures detected. + Data: All data colors identical to light render — #009E73 curve, #4467A3 yield circle, #C475FD UTS triangle, #AE3030 fracture square, #BD8233 offset dashes. Only chrome elements flipped. Brand green #009E73 clearly visible on dark surface. + Legibility verdict: PASS — all text readable against dark background. No dark-on-dark failures. criteria_checklist: visual_quality: - score: 29 + score: 28 max: 30 items: - id: VQ-01 name: Text Legibility - score: 8 + score: 7 max: 8 passed: true - comment: 'All font sizes explicitly set: title 28pt, axis labels 22pt, ticks - 18pt, region labels 22pt, annotations 18pt' + comment: All font sizes explicitly set (50pt title, 42pt axis labels, 34pt + ticks, 28pt point annotations, 26pt modulus). Well-proportioned and readable + in both themes. Minor deduction for dense annotation cluster near compressed + elastic region (left ~2% of x-axis). - id: VQ-02 name: No Overlap - score: 5 + score: 6 max: 6 passed: true - comment: Left side slightly congested with yield marker, offset line, elastic - label, and E annotation clustered together, but all text remains readable + comment: No text overlaps detected in either render. - id: VQ-03 name: Element Visibility - score: 6 + score: 5 max: 6 passed: true - comment: Line width 5 and marker size 32 well-adapted for 4800x2700 + comment: 'Main curve (line_width=5) and key-point markers (size=28) very prominent. + Deduction: 0.2% offset line spans only ~1% of x-axis range (x=0.002 to ~0.005), + appearing as a tiny dashed segment barely distinguishable at chart scale.' - id: VQ-04 name: Color Accessibility - score: 4 - max: 4 + score: 2 + max: 2 passed: true - comment: 'Colorblind-safe palette: blue, gold, purple, sky blue with white - outlines on markers' + comment: Imprint palette is CVD-safe; redundant encoding via distinct marker + shapes (circle/triangle/square) for yield/UTS/fracture. - id: VQ-05 name: Layout & Canvas score: 4 max: 4 passed: true - comment: Good canvas utilization with balanced margins + comment: Canvas well-utilized. Generous min_border margins. Legend sits in + bottom-right without isolating from data. - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: 'Descriptive labels with units: Engineering Strain (mm/mm) and Engineering - Stress (MPa)' + comment: Engineering Strain (mm/mm) and Engineering Stress (MPa) both include + units. + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'Primary curve #009E73 correct. UTS/lavender #C475FD, yield/blue + #4467A3, offset/ochre #BD8233, fracture/matte-red #AE3030 (semantic exception + for failure). Backgrounds #FAF8F1/#1A1A17 correct. Chrome theme-adaptive + in both renders.' design_excellence: - score: 15 + score: 13 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 6 + score: 5 max: 8 passed: true - comment: Custom colorblind-safe palette with intentional color hierarchy, - styled typography, professional polish beyond defaults + comment: 'Above default: semantic red for fracture, distinct marker shapes + for CVD encoding, italic/bold annotation hierarchy, subtle Span reference + lines. Not yet at strong design level (6).' - id: DE-02 name: Visual Refinement - score: 5 + score: 4 max: 6 passed: true - comment: Subtle grid (alpha 0.15), outline removed, minor ticks hidden, styled - background fills, dotted reference lines + comment: Y-axis-only grid at alpha=0.12, L-shaped axis frame (outline removed), + minor ticks removed, semi-transparent elevated legend background. Meaningful + refinement above defaults. - id: DE-03 name: Data Storytelling score: 4 max: 6 passed: true - comment: Region labels and annotated critical points guide viewer through - material behavior; reference lines reinforce key stress levels + comment: 'Clear narrative: region labels guide elastic to strain hardening + to necking; key point annotations connect visual features to engineering + properties; E=210 GPa grounds the elastic region.' spec_compliance: - score: 15 + score: 14 max: 15 items: - id: SC-01 @@ -118,27 +143,31 @@ review: score: 5 max: 5 passed: true - comment: Correct line chart for stress-strain curve + comment: Correct stress-strain line chart with all three mechanical behavior + regions visible. - id: SC-02 name: Required Features - score: 4 + score: 3 max: 4 passed: true - comment: 'All spec features present: region labels, critical points, Young''s - modulus annotation, 0.2% offset line' + comment: 'Region labels, key points, Young''s modulus annotation, 0.2% offset + line all present. Deduction: offset line compressed to ~1% of chart width, + making the spec goal of illustrating yield point determination visually + weak.' - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: X=strain, Y=stress correctly mapped; axes cover full data range + comment: Strain on x-axis, stress on y-axis, all data visible within axis + bounds. - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title format correct; legend labels descriptive and match rendered - elements + comment: Title matches required format. Legend shows all 5 elements with descriptive + labels. data_quality: score: 15 max: 15 @@ -148,22 +177,23 @@ review: score: 6 max: 6 passed: true - comment: Shows all three deformation regions with smooth transitions and critical - points + comment: 'All aspects: elastic region, yield point (0.2% offset method), strain + hardening (Ludwik power law), necking, fracture, Young''s modulus annotation.' - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Mild steel tensile test with physically accurate Ludwik power law - modeling + comment: 'Mild steel tensile test: neutral, real-world engineering scenario + widely taught in materials science.' - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: E=210 GPa, yield=250 MPa, UTS=400 MPa, fracture strain=0.35 — realistic - for mild steel + comment: E=210 GPa correct for steel, yield=250 MPa typical mild steel, UTS=400 + MPa, fracture strain=0.35 within ductile steel range. Ludwik power law model + physically accurate. code_quality: score: 10 max: 10 @@ -173,61 +203,68 @@ review: score: 3 max: 3 passed: true - comment: Linear imports-data-plot-save structure with no functions or classes + comment: 'Clean linear flow: imports → tokens → data → figure → glyphs → annotations + → legend → styling → save.' - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: np.random.seed(42) set + comment: np.random.seed(42) set. - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: All imports used + comment: All imported modules are used. - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Clean, well-organized code with logical sections + comment: HoverTool is native Bokeh interactive functionality, not fake interactivity. + Appropriate complexity for multi-annotated stress-strain plot. - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves as plot.png via export_png and plot.html via save + comment: Saves plot-{THEME}.html and plot-{THEME}.png using current Bokeh + API. library_mastery: - score: 7 + score: 9 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 4 + score: 5 max: 5 passed: true - comment: Good use of ColumnDataSource, Legend/LegendItem, Span, Label — follows - Bokeh patterns + comment: 'Expert usage: ColumnDataSource with custom @region column for hover, + HoverTool in vline mode, Legend+LegendItem manual construction, Span for + reference lines, Label for annotations, add_layout for layout-level elements, + CDN resources for HTML export.' - id: LM-02 name: Distinctive Features - score: 3 + score: 4 max: 5 passed: true - comment: HoverTool with region-aware tooltips (vline mode) and dual PNG+HTML - export - verdict: APPROVED + comment: 'Region-aware interactive hover tooltips (Strain/Stress/Region) distinctively + Bokeh. Dual artifact output (interactive HTML + static PNG). Span reference + lines. Minor deduction: could further leverage Bokeh-specific interactivity.' + verdict: REJECTED impl_tags: - dependencies: [] + dependencies: + - selenium techniques: - - annotations - hover-tooltips - - custom-legend - html-export + - annotations + - custom-legend patterns: - data-generation - columndatasource dataprep: [] styling: - - grid-styling + - alpha-blending - edge-highlighting