{"spec_id":"mohr-circle","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nmohr-circle: Mohr's Circle for Stress Analysis\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-05-30\n\"\"\"\n\nimport os\n\nimport matplotlib.patches as patches\nimport matplotlib.patheffects as patheffects\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n\n# Theme tokens — Imprint palette chrome (data colors stay theme-independent)\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Imprint palette — semantic role assignment for Mohr's circle\nCLR_GEOM = \"#009E73\"  # brand green (position 1) — circle outline, center, angle arc\nCLR_INPUT = \"#AE3030\"  # matte red (semantic: applied stress / force) — input points A, B\nCLR_DERIVED = \"#4467A3\"  # blue (position 3) — principal stresses σ₁, σ₂, τ_max\n\n# Data — steel beam under combined loading (tension + compression + shear)\nsigma_x = 80  # Normal stress in x-direction (MPa)\nsigma_y = -40  # Normal stress in y-direction (MPa)\ntau_xy = 30  # Shear stress on xy-plane (MPa)\n\n# Mohr's circle parameters\ncenter = (sigma_x + sigma_y) / 2\nradius = np.sqrt(((sigma_x - sigma_y) / 2) ** 2 + tau_xy**2)\nsigma_1 = center + radius\nsigma_2 = center - radius\ntau_max = radius\ntheta_2p = np.degrees(np.arctan2(tau_xy, sigma_x - center))\n\n# Circle coordinates\ntheta = np.linspace(0, 2 * np.pi, 360)\ncircle_sigma = center + radius * np.cos(theta)\ncircle_tau = radius * np.sin(theta)\n\n# Shared arrow style\narrow_kw = {\"arrowstyle\": \"-|>\", \"mutation_scale\": 12, \"lw\": 1.2}\nstroke_fx = [patheffects.withStroke(linewidth=2.5, foreground=PAGE_BG), patheffects.Normal()]\n\n# Plot — square canvas for equal-aspect Mohr's circle (2400×2400 px)\nfig, ax = plt.subplots(figsize=(6, 6), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\nax.set_aspect(\"equal\")\n\n# Axis limits with padding for annotations\npad = radius * 0.55\nax.set_xlim(sigma_2 - pad, sigma_1 + pad)\nax.set_ylim(-tau_max - pad, tau_max + pad)\n\n# Subtle circle fill for visual richness\ncircle_fill = patches.Circle((center, 0), radius, facecolor=CLR_GEOM, alpha=0.06, edgecolor=\"none\", zorder=1)\nax.add_patch(circle_fill)\n\n# Mohr's circle outline\nax.plot(circle_sigma, circle_tau, color=CLR_GEOM, linewidth=2.5, zorder=3)\n\n# Reference lines through center\nax.axhline(y=0, color=INK_SOFT, linewidth=0.8, zorder=1)\nax.axvline(x=center, color=INK_SOFT, linewidth=0.8, linestyle=\"--\", alpha=0.5, zorder=1)\n\n# Line connecting stress points A and B (diameter of Mohr's circle)\nax.plot([sigma_x, sigma_y], [tau_xy, -tau_xy], color=CLR_INPUT, linewidth=1.5, linestyle=\"--\", alpha=0.5, zorder=2)\n\n# Stress points A(σx, τxy) and B(σy, −τxy)\nax.scatter([sigma_x, sigma_y], [tau_xy, -tau_xy], color=CLR_INPUT, s=120, edgecolors=PAGE_BG, linewidth=1.2, zorder=5)\na_txt = ax.annotate(\n    f\"A ({sigma_x}, {tau_xy})\",\n    xy=(sigma_x, tau_xy),\n    xytext=(sigma_x + 10, tau_xy + 14),\n    fontsize=10,\n    color=CLR_INPUT,\n    fontweight=\"bold\",\n    arrowprops={**arrow_kw, \"color\": CLR_INPUT},\n)\na_txt.set_path_effects(stroke_fx)\nb_txt = ax.annotate(\n    f\"B ({sigma_y}, {-tau_xy})\",\n    xy=(sigma_y, -tau_xy),\n    xytext=(sigma_y - 10, -tau_xy - 14),\n    fontsize=10,\n    color=CLR_INPUT,\n    fontweight=\"bold\",\n    ha=\"right\",\n    arrowprops={**arrow_kw, \"color\": CLR_INPUT},\n)\nb_txt.set_path_effects(stroke_fx)\n\n# Principal stresses σ₁ and σ₂ (diamond markers for derived quantities)\nax.scatter([sigma_2], [0], color=CLR_DERIVED, s=140, edgecolors=PAGE_BG, linewidth=1.2, zorder=5, marker=\"D\")\n# σ₁ is the critical engineering result — emphasized with a larger marker\nax.scatter([sigma_1], [0], color=CLR_DERIVED, s=200, edgecolors=PAGE_BG, linewidth=1.5, zorder=6, marker=\"D\")\nsigma1_txt = ax.annotate(\n    f\"σ₁ = {sigma_1:.1f} MPa\",\n    xy=(sigma_1, 0),\n    xytext=(sigma_1, -30),\n    fontsize=11,\n    color=CLR_DERIVED,\n    fontweight=\"bold\",\n    ha=\"center\",\n    arrowprops={**arrow_kw, \"color\": CLR_DERIVED},\n    bbox={\n        \"boxstyle\": \"round,pad=0.3\",\n        \"facecolor\": ELEVATED_BG,\n        \"edgecolor\": CLR_DERIVED,\n        \"alpha\": 0.7,\n        \"linewidth\": 1.2,\n    },\n)\nsigma1_txt.set_path_effects(stroke_fx)\nsigma2_txt = ax.annotate(\n    f\"σ₂ = {sigma_2:.1f} MPa\",\n    xy=(sigma_2, 0),\n    xytext=(sigma_2, 22),\n    fontsize=10,\n    color=CLR_DERIVED,\n    fontweight=\"bold\",\n    ha=\"center\",\n    arrowprops={**arrow_kw, \"color\": CLR_DERIVED},\n)\nsigma2_txt.set_path_effects(stroke_fx)\n\n# Maximum shear stress τ_max at top and bottom — text moved left to clear upper-right region\nax.scatter(\n    [center, center],\n    [tau_max, -tau_max],\n    color=CLR_DERIVED,\n    s=140,\n    edgecolors=PAGE_BG,\n    linewidth=1.2,\n    zorder=5,\n    marker=\"D\",\n)\ntmax_txt = ax.annotate(\n    f\"τ_max = {tau_max:.1f} MPa\",\n    xy=(center, tau_max),\n    xytext=(center - 38, tau_max + 12),\n    fontsize=10,\n    color=CLR_DERIVED,\n    fontweight=\"bold\",\n    arrowprops={**arrow_kw, \"color\": CLR_DERIVED},\n)\ntmax_txt.set_path_effects(stroke_fx)\ntmin_txt = ax.annotate(\n    f\"−τ_max = −{tau_max:.1f} MPa\",\n    xy=(center, -tau_max),\n    xytext=(center - 38, -tau_max - 12),\n    fontsize=10,\n    color=CLR_DERIVED,\n    fontweight=\"bold\",\n    arrowprops={**arrow_kw, \"color\": CLR_DERIVED},\n)\ntmin_txt.set_path_effects(stroke_fx)\n\n# Principal angle 2θp arc\narc_radius = radius * 0.35\narc = patches.Arc(\n    (center, 0),\n    2 * arc_radius,\n    2 * arc_radius,\n    angle=0,\n    theta1=0,\n    theta2=theta_2p,\n    color=CLR_GEOM,\n    linewidth=2.0,\n    zorder=4,\n)\nax.add_patch(arc)\n\n# Small arrowhead at arc tip\narc_tip_angle = np.radians(theta_2p)\nax.annotate(\n    \"\",\n    xy=(center + arc_radius * np.cos(arc_tip_angle), arc_radius * np.sin(arc_tip_angle)),\n    xytext=(center + arc_radius * np.cos(arc_tip_angle - 0.08), arc_radius * np.sin(arc_tip_angle - 0.08)),\n    arrowprops={\"arrowstyle\": \"-|>\", \"color\": CLR_GEOM, \"lw\": 1.5, \"mutation_scale\": 14},\n    zorder=4,\n)\n\n# Angle label — lower-right to avoid crowding with τ_max labels in the upper region\narc_mid = np.radians(theta_2p / 2)\narc_txt = ax.annotate(\n    f\"2θp = {theta_2p:.1f}°\",\n    xy=(center + arc_radius * np.cos(arc_mid), arc_radius * np.sin(arc_mid)),\n    xytext=(center + arc_radius * 2.8, arc_radius * 0.4),\n    fontsize=10,\n    color=CLR_GEOM,\n    fontweight=\"bold\",\n    arrowprops={**arrow_kw, \"color\": CLR_GEOM},\n)\narc_txt.set_path_effects(stroke_fx)\n\n# Center marker with theme-adaptive path effect for readability\nax.plot(center, 0, marker=\"+\", color=CLR_GEOM, markersize=14, markeredgewidth=2.0, zorder=5)\ncenter_txt = ax.annotate(\n    f\"C ({center:.0f}, 0)\",\n    xy=(center, 0),\n    xytext=(center - 8, -14),\n    fontsize=10,\n    color=CLR_GEOM,\n    ha=\"right\",\n    fontweight=\"bold\",\n)\ncenter_txt.set_path_effects(stroke_fx)\n\n# Style — theme-adaptive chrome\ntitle = \"mohr-circle · python · matplotlib · anyplot.ai\"\nax.set_xlabel(\"Normal Stress σ (MPa)\", fontsize=10, color=INK)\nax.set_ylabel(\"Shear Stress τ (MPa)\", fontsize=10, color=INK)\nax.set_title(title, fontsize=12, fontweight=\"medium\", color=INK, pad=15)\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT)\nfor spine in (\"left\", \"bottom\"):\n    ax.spines[spine].set_color(INK_SOFT)\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax.grid(True, alpha=0.15, linewidth=0.6, color=INK)\n\n# Save — bbox_inches must stay default (None) to preserve exact 2400×2400 canvas\nfig.subplots_adjust(left=0.12, right=0.95, bottom=0.10, top=0.93)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}