From eab8888b9c1dc6bdf3d51c29f1cfeae5188ffd31 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 21 Jun 2026 09:44:20 +0000 Subject: [PATCH 1/4] feat(letsplot): implement line-stress-strain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regen from quality 92. Addressed: - Canvas: fixed ggsize(1600,900)+scale=3 → ggsize(800,450)+scale=4 (3200×1800) - ANYPLOT_THEME support: added theme-adaptive chrome tokens (PAGE_BG, INK, INK_SOFT, INK_MUTED) - Output filenames: plot.png → plot-{THEME}.png, plot.html → plot-{THEME}.html - Imprint palette: replaced non-Imprint colors with BRAND (#009E73 first series), COLOR_OFFSET (#C475FD), COLOR_YIELD (#4467A3), COLOR_UTS (#AE3030), COLOR_FRACTURE (#BD8233) - Region backgrounds: theme-adaptive light/dark tints (was hardcoded light-only) - geom_text sizes: corrected from 9-12 (pt scale) to 3.5-4 (mm range for 800×450 base) - element_text sizes: axis_title 12pt, axis_text 10pt, plot_title 16pt (canonical values) - Title format: line-stress-strain · python · letsplot · anyplot.ai --- .../implementations/python/letsplot.py | 205 ++++++++---------- 1 file changed, 95 insertions(+), 110 deletions(-) diff --git a/plots/line-stress-strain/implementations/python/letsplot.py b/plots/line-stress-strain/implementations/python/letsplot.py index ec09ea135e..7f8aec5b0c 100644 --- a/plots/line-stress-strain/implementations/python/letsplot.py +++ b/plots/line-stress-strain/implementations/python/letsplot.py @@ -1,84 +1,94 @@ -""" pyplots.ai +"""anyplot.ai line-stress-strain: Engineering Stress-Strain Curve Library: letsplot 4.9.0 | Python 3.14.3 -Quality: 92/100 | Created: 2026-03-20 +Quality: 92/100 | Updated: 2026-06-21 """ +import os + import numpy as np import pandas as pd from lets_plot import * # noqa: F403 -from lets_plot.export import ggsave as export_ggsave +from lets_plot.export import ggsave LetsPlot.setup_html() # noqa: F405 -# Data - Mild steel tensile test simulation +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 positions +BRAND = "#009E73" # green — main stress-strain curve (first series) +COLOR_OFFSET = "#C475FD" # lavender — 0.2% offset line +COLOR_YIELD = "#4467A3" # blue — yield point +COLOR_UTS = "#AE3030" # matte red — UTS (semantic: peak stress / failure onset) +COLOR_FRACTURE = "#BD8233" # ochre — fracture point + +# Theme-adaptive region background tints derived from Imprint palette +if THEME == "light": + region_fill = {"Elastic": "#D6EEE6", "Strain Hardening": "#D6DDF0", "Necking": "#F0D6D6"} +else: + region_fill = {"Elastic": "#172920", "Strain Hardening": "#171C2C", "Necking": "#2C1717"} + +# Data — Mild steel tensile test simulation np.random.seed(42) - -# Material properties for mild steel youngs_modulus = 210000 # MPa yield_strength = 250 # MPa uts = 400 # MPa (ultimate tensile strength) fracture_strain = 0.35 uts_strain = 0.22 -yield_strain = yield_strength / youngs_modulus # ~0.00119 +yield_strain = yield_strength / youngs_modulus # Elastic region (0 to yield) -n_elastic = 60 -strain_elastic = np.linspace(0, yield_strain, n_elastic) +strain_elastic = np.linspace(0, yield_strain, 60) stress_elastic = youngs_modulus * strain_elastic -# Yield plateau (mild steel has a distinct yield point) -n_plateau = 20 -strain_plateau = np.linspace(yield_strain, 0.015, n_plateau) -stress_plateau = yield_strength + np.random.normal(0, 1.5, n_plateau) +# Yield plateau (mild steel distinct yield point) +strain_plateau = np.linspace(yield_strain, 0.015, 20) +stress_plateau = yield_strength + np.random.normal(0, 1.5, 20) -# Strain hardening region (from end of plateau to UTS) -n_hardening = 120 -strain_hardening = np.linspace(0.015, uts_strain, n_hardening) +# Strain hardening +strain_hardening = np.linspace(0.015, uts_strain, 120) stress_hardening = yield_strength + (uts - yield_strength) * ( 1 - np.exp(-8 * (strain_hardening - 0.015) / (uts_strain - 0.015)) ) -stress_hardening += np.random.normal(0, 1.0, n_hardening) +stress_hardening += np.random.normal(0, 1.0, 120) -# Necking region (UTS to fracture) -n_necking = 60 -strain_necking = np.linspace(uts_strain, fracture_strain, n_necking) +# Necking (UTS to fracture) +strain_necking = np.linspace(uts_strain, fracture_strain, 60) stress_necking = uts - (uts - 280) * ((strain_necking - uts_strain) / (fracture_strain - uts_strain)) ** 1.5 -stress_necking += np.random.normal(0, 1.5, n_necking) +stress_necking += np.random.normal(0, 1.5, 60) -# Combine all regions strain = np.concatenate([strain_elastic, strain_plateau, strain_hardening, strain_necking]) stress = np.concatenate([stress_elastic, stress_plateau, stress_hardening, stress_necking]) - df = pd.DataFrame({"strain": strain, "stress": stress}) # 0.2% offset line for yield point determination offset_val = 0.002 -offset_line_strain = np.linspace(offset_val, offset_val + yield_strength / youngs_modulus + 0.003, 50) -offset_line_stress = youngs_modulus * (offset_line_strain - offset_val) -offset_line_stress = np.clip(offset_line_stress, 0, yield_strength + 30) -df_offset = pd.DataFrame({"strain": offset_line_strain, "stress": offset_line_stress}) +offset_strain = np.linspace(offset_val, offset_val + yield_strength / youngs_modulus + 0.003, 50) +offset_stress = np.clip(youngs_modulus * (offset_strain - offset_val), 0, yield_strength + 30) +df_offset = pd.DataFrame({"strain": offset_strain, "stress": offset_stress}) # Key points yield_point_strain = offset_val + yield_strength / youngs_modulus -yield_point_stress = yield_strength fracture_stress = stress_necking[-1] - df_points = pd.DataFrame( { "strain": [yield_point_strain, uts_strain, fracture_strain], - "stress": [yield_point_stress, uts, fracture_stress], - "label": [f"Yield Point ({yield_strength} MPa)", f"UTS ({uts} MPa)", f"Fracture ({fracture_stress:.0f} MPa)"], + "stress": [yield_strength, uts, fracture_stress], "type": ["Yield", "UTS", "Fracture"], } ) -# Consolidated annotations DataFrame +# Annotation labels df_annotations = pd.DataFrame( { "x": [yield_point_strain + 0.012, uts_strain + 0.015, fracture_strain - 0.045, 0.008, 0.007, 0.005, 0.11, 0.29], - "y": [yield_point_stress + 15, uts + 10, fracture_stress - 30, 130, 60, 350, 350, 350], + "y": [yield_strength + 15, uts + 10, fracture_stress - 30, 130, 60, 350, 350, 310], "label": [ f"Yield Point\n({yield_strength} MPa)", f"UTS ({uts} MPa)", @@ -86,155 +96,130 @@ f"E = {youngs_modulus // 1000} GPa", "0.2% offset", "Elastic", - "Strain Hardening", + "Strain\nHardening", "Necking", ], "group": ["yield", "uts", "fracture", "modulus", "offset", "region", "region", "region"], } ) -# Colorblind-safe palette: blue, purple, gray (avoids orange/green pair) -color_yield = "#9467BD" # purple -color_uts = "#D62728" # red -color_fracture = "#7F7F7F" # gray -color_main = "#306998" # Python blue -color_offset = "#E377C2" # pink - -# Segment connector lines from key points to annotations (distinctive lets-plot feature) +# Connector lines from key points to annotation labels df_segments = pd.DataFrame( { "x": [yield_point_strain, uts_strain, fracture_strain], - "y": [yield_point_stress, uts, fracture_stress], + "y": [yield_strength, uts, fracture_stress], "xend": [yield_point_strain + 0.011, uts_strain + 0.014, fracture_strain - 0.035], - "yend": [yield_point_stress + 12, uts + 8, fracture_stress - 22], + "yend": [yield_strength + 12, uts + 8, fracture_stress - 22], + } +) + +# Region background rectangles +df_regions = pd.DataFrame( + { + "xmin": [0, 0.015, uts_strain], + "xmax": [0.015, uts_strain, fracture_strain], + "ymin": [0, 0, 0], + "ymax": [460, 460, 460], + "region": ["Elastic", "Strain Hardening", "Necking"], } ) +# Combined fill color mapping (regions + key points share the fill aesthetic) +fill_colors = {**region_fill, "Yield": COLOR_YIELD, "UTS": COLOR_UTS, "Fracture": COLOR_FRACTURE} + +title = "line-stress-strain · python · letsplot · anyplot.ai" + # Plot plot = ( ggplot() - # Region background bands using geom_rect (distinctive lets-plot feature) - + geom_rect( - aes(xmin="xmin", xmax="xmax", ymin="ymin", ymax="ymax", fill="region"), - data=pd.DataFrame( - { - "xmin": [0, 0.015, uts_strain], - "xmax": [0.015, uts_strain, fracture_strain], - "ymin": [0, 0, 0], - "ymax": [460, 460, 460], - "region": ["Elastic", "Strain Hardening", "Necking"], - } - ), - alpha=0.35, - ) - + scale_fill_manual( - values={ - "Elastic": "#DAE8FC", - "Strain Hardening": "#FFF2CC", - "Necking": "#F8D7DA", - "Yield": color_yield, - "UTS": color_uts, - "Fracture": color_fracture, - } - ) - # Main stress-strain curve with tooltips (distinctive lets-plot feature) + + geom_rect(aes(xmin="xmin", xmax="xmax", ymin="ymin", ymax="ymax", fill="region"), data=df_regions) + geom_line( aes(x="strain", y="stress"), data=df, - color=color_main, - size=2.0, + color=BRAND, + size=1.5, tooltips=layer_tooltips() .format("strain", ".4f") .format("stress", ".1f") .line("Strain: @strain") .line("Stress: @stress MPa"), ) - # 0.2% offset line - + geom_line(aes(x="strain", y="stress"), data=df_offset, color=color_offset, size=1.2, linetype="dashed") - # Segment connectors from points to labels (geom_segment - distinctive feature) + + geom_line(aes(x="strain", y="stress"), data=df_offset, color=COLOR_OFFSET, size=1.0, linetype="dashed") + geom_segment( - aes(x="x", y="y", xend="xend", yend="yend"), data=df_segments, color="#999999", size=0.6, linetype="dotted" + aes(x="x", y="y", xend="xend", yend="yend"), data=df_segments, color=INK_SOFT, size=0.5, linetype="dotted" ) - # Key points with tooltips (distinctive lets-plot feature) + geom_point( aes(x="strain", y="stress", fill="type"), data=df_points, - color="white", - size=7, + color=PAGE_BG, + size=5, shape=21, - stroke=1.2, + stroke=1.5, tooltips=layer_tooltips().line("@type").line("Strain: @strain").line("Stress: @stress MPa"), ) + + scale_fill_manual(values=fill_colors) + guides(fill="none") - # Annotations - key points + geom_text( aes(x="x", y="y", label="label"), data=df_annotations.query("group == 'yield'"), - size=11, - color=color_yield, + size=4, + color=COLOR_YIELD, hjust=0, ) + geom_text( - aes(x="x", y="y", label="label"), data=df_annotations.query("group == 'uts'"), size=11, color=color_uts, hjust=0 + aes(x="x", y="y", label="label"), data=df_annotations.query("group == 'uts'"), size=4, color=COLOR_UTS, hjust=0 ) + geom_text( aes(x="x", y="y", label="label"), data=df_annotations.query("group == 'fracture'"), - size=11, - color=color_fracture, + size=4, + color=COLOR_FRACTURE, hjust=0.5, ) - # Elastic modulus annotation + geom_text( aes(x="x", y="y", label="label"), data=df_annotations.query("group == 'modulus'"), - size=10, - color=color_main, + size=3.5, + color=BRAND, hjust=0, fontface="italic", ) - # Offset label + geom_text( aes(x="x", y="y", label="label"), data=df_annotations.query("group == 'offset'"), - size=9, - color=color_offset, + size=3.5, + color=COLOR_OFFSET, hjust=0, fontface="italic", ) - # Region labels + geom_text( aes(x="x", y="y", label="label"), data=df_annotations.query("group == 'region'"), - size=12, - color="#666666", + size=4, + color=INK_MUTED, fontface="italic", ) - # Styling - + labs( - x="Engineering Strain", - y="Engineering Stress (MPa)", - title="line-stress-strain \u00b7 letsplot \u00b7 pyplots.ai", - ) + + labs(x="Engineering Strain", y="Engineering Stress (MPa)", title=title) + scale_x_continuous(breaks=[0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35]) + scale_y_continuous(breaks=[0, 50, 100, 150, 200, 250, 300, 350, 400, 450]) - + ggsize(1600, 900) + + ggsize(800, 450) + theme_minimal() + theme( - axis_text=element_text(size=16, color="#555555"), - axis_title=element_text(size=20, color="#333333"), - plot_title=element_text(size=24, color="#222222", face="bold"), + plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), + panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG), + axis_title=element_text(size=12, color=INK), + axis_text=element_text(size=10, color=INK_SOFT), + plot_title=element_text(size=16, color=INK), panel_grid_major_x=element_blank(), - panel_grid_major_y=element_line(color="#E0E0E0", size=0.3), + panel_grid_major_y=element_line(color=INK_SOFT, size=0.2), panel_grid_minor=element_blank(), - plot_background=element_rect(fill="#FAFAFA", color="#FAFAFA"), - panel_background=element_rect(fill="transparent", color="transparent"), + axis_line=element_line(color=INK_SOFT), axis_ticks=element_blank(), axis_ticks_length=0, - plot_margin=[30, 40, 20, 20], + plot_margin=[20, 30, 15, 15], ) ) # Save -export_ggsave(plot, filename="plot.png", path=".", scale=3) -export_ggsave(plot, filename="plot.html", path=".") +ggsave(plot, f"plot-{THEME}.png", path=".", scale=4) +ggsave(plot, f"plot-{THEME}.html", path=".") From f0bd12ddb9ec283d45bf316e2c4cdf1e9c8473f5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 21 Jun 2026 09:44:36 +0000 Subject: [PATCH 2/4] chore(letsplot): add metadata for line-stress-strain --- .../metadata/python/letsplot.yaml | 240 ++---------------- 1 file changed, 16 insertions(+), 224 deletions(-) diff --git a/plots/line-stress-strain/metadata/python/letsplot.yaml b/plots/line-stress-strain/metadata/python/letsplot.yaml index caf622ac75..de1015713d 100644 --- a/plots/line-stress-strain/metadata/python/letsplot.yaml +++ b/plots/line-stress-strain/metadata/python/letsplot.yaml @@ -1,229 +1,21 @@ +# Per-library metadata for letsplot implementation of line-stress-strain +# Auto-generated by impl-generate.yml + library: letsplot +language: python specification_id: line-stress-strain created: '2026-03-20T21:25:05Z' -updated: '2026-03-20T21:53:26Z' -generated_by: claude-opus-4-5-20251101 -workflow_run: 23363004980 +updated: '2026-06-21T09:44:36Z' +generated_by: claude-sonnet +workflow_run: 27900235161 issue: 4413 -python_version: 3.14.3 -library_version: 4.9.0 -preview_url: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/letsplot/plot.png -preview_html: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/letsplot/plot.html -quality_score: 92 +language_version: 3.13.14 +library_version: 4.10.1 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/python/letsplot/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/python/letsplot/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/python/letsplot/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/python/letsplot/plot-dark.html +quality_score: null review: - strengths: - - Excellent data storytelling with colored region bands that immediately communicate - the three deformation phases - - All spec requirements fully implemented including 0.2% offset line and elastic - modulus annotation - - Textbook-accurate mild steel properties with realistic noise - - Strong lets-plot idiomatic usage with distinctive tooltip features - - Clean, well-organized code with consolidated annotation DataFrames - weaknesses: - - Minor crowding of annotations in the elastic region (small area with multiple - labels) - - Red/purple color pair could be slightly improved for colorblind accessibility - image_description: 'The plot displays an engineering stress-strain curve for mild - steel on a light gray background. The x-axis shows "Engineering Strain" (0 to - 0.35) and the y-axis shows "Engineering Stress (MPa)" (0 to 450). The main curve - is drawn as a thick blue line starting from the origin, rising steeply through - the elastic region, showing a brief yield plateau around 250 MPa, then curving - through strain hardening up to ~400 MPa (UTS), and finally declining through the - necking region to fracture at ~280 MPa. Three colored background bands distinguish - the regions: light blue for Elastic, light yellow for Strain Hardening, and light - pink for Necking, each labeled in gray italic text. Key points are marked with - filled circles: purple for Yield Point (250 MPa), red for UTS (400 MPa), and gray - for Fracture. A dashed pink line shows the 0.2% offset construction, and "E = - 210 GPa" is annotated in blue italic. Dotted gray connector lines link points - to their labels. The title reads "line-stress-strain · letsplot · pyplots.ai" - in bold at the top. Only horizontal grid lines are shown, subtle and light.' - criteria_checklist: - visual_quality: - score: 27 - max: 30 - items: - - id: VQ-01 - name: Text Legibility - score: 7 - max: 8 - passed: true - comment: All major text sizes explicitly set (title 24, axis title 20, axis - text 16). Annotation sizes 9-11 slightly small but readable. - - id: VQ-02 - name: No Overlap - score: 5 - max: 6 - passed: true - comment: Minor crowding in elastic region where multiple annotations are close - together, but all readable. - - id: VQ-03 - name: Element Visibility - score: 6 - max: 6 - passed: true - comment: Main curve thick and visible. Key points use large filled circles - with white stroke. Offset line distinct. - - id: VQ-04 - name: Color Accessibility - score: 3 - max: 4 - passed: true - comment: Blue, purple, red, gray, pink palette avoids red-green. Red/purple - pair slightly challenging for some protanopia. - - id: VQ-05 - name: Layout & Canvas - score: 4 - max: 4 - passed: true - comment: Plot fills canvas well with balanced margins at 1600x900 scaled 3x. - - id: VQ-06 - name: Axis Labels & Title - score: 2 - max: 2 - passed: true - comment: Engineering Strain and Engineering Stress (MPa) - descriptive with - units. - design_excellence: - score: 16 - max: 20 - items: - - id: DE-01 - name: Aesthetic Sophistication - score: 6 - max: 8 - passed: true - comment: Custom color palette, colored region bands, italic labels, connector - lines show design thought beyond defaults. - - id: DE-02 - name: Visual Refinement - score: 5 - max: 6 - passed: true - comment: Minimal theme, removed x-grid, subtle y-grid, removed ticks, custom - background, transparent panel. - - id: DE-03 - name: Data Storytelling - score: 5 - max: 6 - passed: true - comment: Colored region bands communicate deformation phases. Annotated key - points guide viewer through material behavior. - spec_compliance: - score: 15 - max: 15 - items: - - id: SC-01 - name: Plot Type - score: 5 - max: 5 - passed: true - comment: Correct line plot showing stress-strain curve. - - id: SC-02 - name: Required Features - score: 4 - max: 4 - passed: true - comment: 'All required: region labels, critical points, elastic modulus, 0.2% - offset line.' - - id: SC-03 - name: Data Mapping - score: 3 - max: 3 - passed: true - comment: X=strain, Y=stress correctly mapped. Axes cover full range. - - id: SC-04 - name: Title & Legend - score: 3 - max: 3 - passed: true - comment: Title format correct. Legend suppressed in favor of direct annotations. - data_quality: - score: 15 - max: 15 - items: - - id: DQ-01 - name: Feature Coverage - score: 6 - max: 6 - passed: true - comment: 'Shows all regions: elastic, yield plateau, strain hardening, necking - to fracture.' - - id: DQ-02 - name: Realistic Context - score: 5 - max: 5 - passed: true - comment: 'Mild steel with accurate properties: E=210 GPa, yield=250 MPa, UTS=400 - MPa.' - - id: DQ-03 - name: Appropriate Scale - score: 4 - max: 4 - passed: true - comment: All values textbook-accurate for mild steel. - code_quality: - score: 10 - max: 10 - items: - - id: CQ-01 - name: KISS Structure - score: 3 - max: 3 - passed: true - comment: Linear flow with no functions or classes. - - 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: All imports used. - - id: CQ-04 - name: Code Elegance - score: 2 - max: 2 - passed: true - comment: Well-organized, clear variable names, consolidated annotations DataFrame. - - id: CQ-05 - name: Output & API - score: 1 - max: 1 - passed: true - comment: Saves as plot.png via export_ggsave with scale=3. - library_mastery: - score: 9 - max: 10 - items: - - id: LM-01 - name: Idiomatic Usage - score: 5 - max: 5 - passed: true - comment: Expert ggplot grammar with layered geoms, aes, scales, themes. - - id: LM-02 - name: Distinctive Features - score: 4 - max: 5 - passed: true - comment: Uses layer_tooltips, geom_rect, geom_segment, HTML export. Missing - more advanced features. - verdict: APPROVED -impl_tags: - dependencies: [] - techniques: - - annotations - - layer-composition - - hover-tooltips - - html-export - patterns: - - data-generation - dataprep: [] - styling: - - grid-styling - - alpha-blending - - edge-highlighting + strengths: [] + weaknesses: [] From db6c81621ae1e2134bd06a7ccbb4a6e627c82c1a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 21 Jun 2026 09:52:28 +0000 Subject: [PATCH 3/4] chore(letsplot): update quality score 86 and review feedback for line-stress-strain --- .../implementations/python/letsplot.py | 6 +- .../metadata/python/letsplot.yaml | 292 +++++++++++++++++- 2 files changed, 288 insertions(+), 10 deletions(-) diff --git a/plots/line-stress-strain/implementations/python/letsplot.py b/plots/line-stress-strain/implementations/python/letsplot.py index 7f8aec5b0c..6ef6392625 100644 --- a/plots/line-stress-strain/implementations/python/letsplot.py +++ b/plots/line-stress-strain/implementations/python/letsplot.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai line-stress-strain: Engineering Stress-Strain Curve -Library: letsplot 4.9.0 | Python 3.14.3 -Quality: 92/100 | Updated: 2026-06-21 +Library: letsplot 4.10.1 | Python 3.13.14 +Quality: 86/100 | Updated: 2026-06-21 """ import os diff --git a/plots/line-stress-strain/metadata/python/letsplot.yaml b/plots/line-stress-strain/metadata/python/letsplot.yaml index de1015713d..4e0bbd6d07 100644 --- a/plots/line-stress-strain/metadata/python/letsplot.yaml +++ b/plots/line-stress-strain/metadata/python/letsplot.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for letsplot implementation of line-stress-strain -# Auto-generated by impl-generate.yml - library: letsplot language: python specification_id: line-stress-strain created: '2026-03-20T21:25:05Z' -updated: '2026-06-21T09:44:36Z' +updated: '2026-06-21T09:52:28Z' generated_by: claude-sonnet workflow_run: 27900235161 issue: 4413 @@ -15,7 +12,288 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/line-stre preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/python/letsplot/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/python/letsplot/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/line-stress-strain/python/letsplot/plot-dark.html -quality_score: null +quality_score: 86 review: - strengths: [] - weaknesses: [] + strengths: + - 'Perfect spec compliance: all four deformation regions labeled, all three critical + points (yield, UTS, fracture) marked with semantic Imprint palette colors, 0.2% + offset line drawn, Young''s modulus annotated' + - 'Excellent data quality: realistic mild steel mechanical properties (E=210 GPa, + yield=250 MPa, UTS=400 MPa, elongation=35%) with physically accurate stress-strain + curve shape including yield plateau' + - 'Strong data storytelling: color-coded background regions with italic labels, + connector lines from markers to annotations, and semantic color choices (red for + UTS/failure, ochre for fracture) guide viewer through the engineering narrative' + - 'Clean code structure: KISS pattern, np.random.seed(42) for reproducibility, all + imports used, appropriate complexity for the multi-layered visualization' + - 'Correct use of letsplot-distinctive features: layer_tooltips() for interactive + hover info on both curve and key points, HTML export alongside PNG' + - 'Theme-adaptive chrome correct in both renders: backgrounds, text, grid all adapt + properly; data colors (Imprint palette) stay identical across light and dark themes' + - 'Full Imprint palette compliance: main curve #009E73, offset line #C475FD, yield + #4467A3, fracture #BD8233, UTS #AE3030 — all semantic assignments from the palette' + weaknesses: + - 'Elastic region annotation crowding: the elastic zone spans only ~4% of the x-axis + width (0 to 0.015 strain), but contains 4+ text annotations (''Elastic'' label + at y=350, ''E = 210 GPa'' at y=130, ''0.2% offset'' at y=60, ''Yield Point\n(250 + MPa)'' at x≈0.015), the yield point marker, and the dashed offset line — all compressed + together causing near-overlap and readability issues. Fix: increase elastic region + label fontsizes are small enough to not overlap, or reposition the ''Yield Point'' + annotation further right (x≈0.025) and the ''E = GPa'' label higher (y≈180), or + spread annotations more carefully.' + - 'Region fills visually too heavy: the strain hardening background (large blue + rectangle covering 60%+ of plot area) and necking background (pink/rose) dominate + the visual hierarchy, competing with the main data curve. Fix: reduce region fill + alpha to 0.15–0.25 (from the current opaque fill) so the curve and annotations + read more clearly against the subtler background.' + - 'Four-sided box frame from region rectangles: geom_rect with ymax=460 (above the + y-axis max of 450) creates visible borders at all four sides of the plot area, + including top and right — contradicting the preferred L-shaped or minimal spine + look. Fix: set region ymax to match the y-axis limit (e.g., 455), and consider + adding alpha to region fills to soften the framing effect.' + - 'VQ-02 elastic zone crowding: ''Elastic'' italic text at (x=0.008, y=350) appears + to nearly overlap the elastic/strain-hardening boundary vertical edge, and ''Yield + Point\n(250 MPa)'' at (x=0.015+0.012, y=265) is very close to the transition and + the main curve — in narrow regions the text density causes partial overlap.' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white (#FAF8F1) — correct surface. Three color-coded background regions visible: light mint-green for elastic zone (very narrow, 0–0.015 strain), light blue for strain hardening zone (dominant, 0.015–0.22), light pink/rose for necking zone (0.22–0.35). + Chrome: Title "line-stress-strain · python · letsplot · anyplot.ai" in dark ink at top — clearly readable. Y-axis label "Engineering Stress (MPa)" and X-axis label "Engineering Strain" both in dark ink — readable. Tick labels in dark secondary ink — readable. Y-axis-only grid, very subtle. + Data: Main stress-strain curve in brand green (#009E73). Yield point marker in blue (#4467A3). UTS marker in matte red (#AE3030). Fracture marker in ochre (#BD8233). Lavender dashed 0.2% offset line in narrow elastic zone. Annotations for all key points and regions. All elements visible. + Notable issue: The elastic region (0–0.015 strain) is very narrow relative to the full x-axis, causing significant crowding: the lavender offset line, the "Elastic" italic label, "E = 210 GPa", "0.2% offset" annotations, yield point marker, and "Yield Point (250 MPa)" annotation are all compressed into ~4% of the x-axis width. "Elastic" label and yield-point text appear to overlap in this zone. + Legibility verdict: PASS overall, but CROWDED in elastic region. + + Dark render (plot-dark.png): + Background: Warm near-black (#1A1A17) — correct surface. Region fills adapt to dark theme: dark forest-green (elastic), dark navy (strain hardening), dark maroon (necking). + Chrome: Title in light cream — readable. Axis labels in light ink — readable. Tick labels in light secondary ink — readable. Grid lines subtle. All chrome correctly adapts to dark surface. No dark-on-dark failures observed. + Data: Main curve remains brand green (#009E73) — identical to light render. Key point markers retain their colors (blue/red/ochre) — identical to light render. Imprint data colors are unchanged between themes as required. + Legibility verdict: PASS — all text readable against dark background. Theme adaptation correct. + criteria_checklist: + visual_quality: + score: 24 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: All font sizes explicitly set (plot_title=16, axis_title=12, axis_text=10, + geom_text 3.5-4mm). Readable in both themes. Minor deduction for small geom_text + annotations (size=3.5mm) in the narrow elastic region which may be borderline + at mobile scale. + - id: VQ-02 + name: No Overlap + score: 3 + max: 6 + passed: false + comment: Elastic region (0–0.015 strain = ~4% of x-axis) contains 4+ text + annotations plus yield point marker and offset line, causing crowding and + near-overlap. 'Elastic' italic label, 'E=210 GPa', '0.2% offset', and 'Yield + Point (250 MPa)' are all compressed into a narrow column. Minimal overlap + but difficult to read distinctly. + - id: VQ-03 + name: Element Visibility + score: 5 + max: 6 + passed: true + comment: Main curve clearly visible at size=1.5. Key point markers at size=5 + with stroke=1.5 are well visible. Offset line (dashed lavender) is short + and in crowded zone but detectable. Minor deduction for offset line visibility + in the narrow elastic region. + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Imprint palette CVD-safe. All key elements have redundant encoding + (position + color + text label). White edge on key point markers (color=PAGE_BG) + adds definition. + - id: VQ-05 + name: Layout & Canvas + score: 3 + max: 4 + passed: true + comment: 'Canvas 3200x1800 correct. Data fills plot area well. Minor deduction: + geom_rect with ymax=460 extends past y-axis maximum (450), creating visible + filled borders at top/right of plot area from the region fills — produces + unintended four-sided box frame effect.' + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: 'X: ''Engineering Strain'' (dimensionless — correct, no unit needed). + Y: ''Engineering Stress (MPa)'' with units. Title: correct format ''line-stress-strain + · python · letsplot · anyplot.ai''.' + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First series (main curve) = #009E73 brand green. Offset line = #C475FD + (position 2). Yield = #4467A3 (position 3). Fracture = #BD8233 (position + 4). UTS = #AE3030 (position 5, semantic red for failure/peak). Backgrounds + correct: #FAF8F1 light, #1A1A17 dark. Chrome adapts correctly in both renders.' + design_excellence: + score: 13 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: 'Above configured defaults: semantic color assignments for critical + points, region backgrounds, connector lines, multi-layer approach. However, + region fills are too visually heavy, especially the dominant blue strain + hardening zone. The framed-box look reduces polish. Not publication-ready.' + - id: DE-02 + name: Visual Refinement + score: 3 + max: 6 + passed: false + comment: 'Positive: y-axis-only subtle grid (size=0.2), axis_ticks=element_blank(), + theme_minimal() base. Negative: geom_rect fills create visible 4-sided border + frame (top+right visible) contradicting L-spine recommendation. Region fills + too opaque, dominating over data. Some margin adjustment present.' + - id: DE-03 + name: Data Storytelling + score: 5 + max: 6 + passed: true + comment: 'Strong storytelling: color-coded phases, all critical material points + labeled with semantic colors (yield=blue, UTS=red/failure, fracture=ochre), + 0.2% offset method illustrated, Young''s modulus annotated. Viewer immediately + understands the engineering deformation narrative. Clear focal point on + the stress-strain curve.' + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct line chart showing engineering stress vs. engineering strain. + Full stress-strain curve with all characteristic regions. + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: 'All spec requirements present: elastic/plastic/necking region labels, + yield point via 0.2% offset method, UTS marked, fracture point marked, Young''s + modulus annotation (E=210 GPa text), 0.2% offset line drawn.' + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: X=Engineering Strain, Y=Engineering Stress (MPa). All data visible + across full range 0–0.35 strain, 0–450 MPa. + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title 'line-stress-strain · python · letsplot · anyplot.ai' — correct + format. No legend needed (guides(fill='none') suppresses fill legend; key + points labeled inline via geom_text). + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: 'Shows all aspects of stress-strain behavior: elastic region, distinct + yield plateau (mild steel characteristic), strain hardening, necking, all + four critical points. Complete feature coverage.' + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Mild steel tensile test — a real, neutral, engineering education + context. Non-controversial domain. + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: 'All values factually correct: E=210 GPa (steel), yield=250 MPa (low-carbon + steel), UTS=400 MPa (realistic mild steel), fracture at 35% elongation (typical + mild steel ductility). Factually accurate.' + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: Clear imports → data → plot → save structure. No functions or classes. + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: np.random.seed(42) set before all random data generation. + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: 'All imports used: os, numpy, pandas, lets_plot.*, lets_plot.export.ggsave.' + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Well-organized, appropriate complexity for the multi-element visualization. + No over-engineering. + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves plot-{THEME}.png and plot-{THEME}.html. Current letsplot API. + library_mastery: + score: 9 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 5 + max: 5 + passed: true + comment: 'Expert ggplot grammar usage: multiple geom layers composed correctly, + scale_fill_manual, guides(), theme customization with element_rect/element_text/element_line/element_blank. + geom_text with filtered data via .query().' + - id: LM-02 + name: Distinctive Features + score: 4 + max: 5 + passed: true + comment: 'Uses layer_tooltips() with .format() and .line() — a distinctive + letsplot feature for custom interactive tooltip formatting on both the main + curve and key point markers. HTML export alongside PNG. Minor deduction: + could further leverage letsplot-specific features.' + verdict: REJECTED +impl_tags: + dependencies: [] + techniques: + - annotations + - hover-tooltips + - html-export + - layer-composition + patterns: + - data-generation + dataprep: [] + styling: + - edge-highlighting From 55d950cd7ee2b237e5e69f991a1b65a3359b208a Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2026 09:56:44 +0000 Subject: [PATCH 4/4] fix(letsplot): address review feedback for line-stress-strain Attempt 1/3 - fixes based on AI review --- .../implementations/python/letsplot.py | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/plots/line-stress-strain/implementations/python/letsplot.py b/plots/line-stress-strain/implementations/python/letsplot.py index 6ef6392625..a5a7470ad0 100644 --- a/plots/line-stress-strain/implementations/python/letsplot.py +++ b/plots/line-stress-strain/implementations/python/letsplot.py @@ -1,4 +1,4 @@ -""" anyplot.ai +"""anyplot.ai line-stress-strain: Engineering Stress-Strain Curve Library: letsplot 4.10.1 | Python 3.13.14 Quality: 86/100 | Updated: 2026-06-21 @@ -87,8 +87,17 @@ # Annotation labels df_annotations = pd.DataFrame( { - "x": [yield_point_strain + 0.012, uts_strain + 0.015, fracture_strain - 0.045, 0.008, 0.007, 0.005, 0.11, 0.29], - "y": [yield_strength + 15, uts + 10, fracture_stress - 30, 130, 60, 350, 350, 310], + "x": [ + yield_point_strain + 0.025, + uts_strain + 0.015, + fracture_strain - 0.045, + 0.002, + 0.007, + 0.0075, + 0.11, + 0.29, + ], + "y": [yield_strength + 15, uts + 10, fracture_stress - 30, 180, 60, 375, 350, 310], "label": [ f"Yield Point\n({yield_strength} MPa)", f"UTS ({uts} MPa)", @@ -108,7 +117,7 @@ { "x": [yield_point_strain, uts_strain, fracture_strain], "y": [yield_strength, uts, fracture_stress], - "xend": [yield_point_strain + 0.011, uts_strain + 0.014, fracture_strain - 0.035], + "xend": [yield_point_strain + 0.023, uts_strain + 0.014, fracture_strain - 0.035], "yend": [yield_strength + 12, uts + 8, fracture_stress - 22], } ) @@ -119,7 +128,7 @@ "xmin": [0, 0.015, uts_strain], "xmax": [0.015, uts_strain, fracture_strain], "ymin": [0, 0, 0], - "ymax": [460, 460, 460], + "ymax": [450, 450, 450], "region": ["Elastic", "Strain Hardening", "Necking"], } ) @@ -132,7 +141,7 @@ # Plot plot = ( ggplot() - + geom_rect(aes(xmin="xmin", xmax="xmax", ymin="ymin", ymax="ymax", fill="region"), data=df_regions) + + geom_rect(aes(xmin="xmin", xmax="xmax", ymin="ymin", ymax="ymax", fill="region"), data=df_regions, alpha=0.2) + geom_line( aes(x="strain", y="stress"), data=df,