{"spec_id":"mohr-circle","library":"echarts","language":"javascript","code":"// anyplot.ai\n// mohr-circle: Mohr's Circle for Stress Analysis\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-08-26\n//# anyplot-orientation: square\n\nconst t = window.ANYPLOT_TOKENS;\nconst { width: W, height: H } = window.ANYPLOT_SIZE;\n\n// --- Data: bridge girder under combined bending + shear (MPa) ---------------\nconst sigmaX = 55;\nconst sigmaY = -15;\nconst tauXY = 30;\n\nconst center = (sigmaX + sigmaY) / 2;\nconst radius = Math.sqrt(((sigmaX - sigmaY) / 2) ** 2 + tauXY ** 2);\nconst sigma1 = center + radius;\nconst sigma2 = center - radius;\nconst tauMax = radius;\nconst theta2p = (Math.atan2(tauXY, sigmaX - center) * 180) / Math.PI;\n\n// Mohr's circle outline (parametric)\nconst circlePoints = [];\nfor (let deg = 0; deg <= 360; deg++) {\n  const rad = (deg * Math.PI) / 180;\n  circlePoints.push([center + radius * Math.cos(rad), radius * Math.sin(rad)]);\n}\n\n// Principal-angle arc, from the reference points to the σ1 axis\nconst arcRadius = radius * 0.4;\nconst arcSteps = 40;\nconst arcPoints = [];\nfor (let i = 0; i <= arcSteps; i++) {\n  const deg = (theta2p * i) / arcSteps;\n  const rad = (deg * Math.PI) / 180;\n  arcPoints.push([center + arcRadius * Math.cos(rad), arcRadius * Math.sin(rad)]);\n}\nconst arcMidRad = ((theta2p / 2) * Math.PI) / 180;\nconst arcLabelPos = [center + arcRadius * 3.1 * Math.cos(arcMidRad), arcRadius * 1.6 * Math.sin(arcMidRad)];\n\n// Equal padding on both axes keeps the circle a true circle: the σ-span\n// (2×radius) and τ-span (2×radius) are identical by construction, and the\n// grid box below is forced square, so equal source-unit spans map 1:1.\nconst pad = radius * 0.65;\n\n// Imprint palette — canonical ordinal assignment for Mohr's circle\nconst CLR_GEOM = t.palette[0]; // brand green — circle geometry, center, angle arc\nconst CLR_INPUT = t.palette[1]; // lavender — applied stress points A, B\nconst CLR_DERIVED = t.palette[2]; // blue — derived principal stresses / tau_max\n\n// Square grid box centered in the mount so xAxis/yAxis spans render 1:1\nconst gridSide = Math.min(W, H) - 260;\nconst gridLeft = (W - gridSide) / 2 + 10;\nconst gridTop = (H - gridSide) / 2 + 20;\n\nconst chart = echarts.init(document.getElementById(\"container\"));\n\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  color: t.palette,\n  title: {\n    text: \"mohr-circle · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: 500 },\n  },\n  grid: { left: gridLeft, top: gridTop, width: gridSide, height: gridSide },\n  xAxis: {\n    type: \"value\",\n    name: \"Normal Stress σ (MPa)\",\n    nameLocation: \"middle\",\n    nameGap: 42,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    min: sigma2 - pad,\n    max: sigma1 + pad,\n    axisLabel: { color: t.inkSoft, fontSize: 14, formatter: (v) => Math.round(v) },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { show: true, lineStyle: { color: t.grid } },\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"Shear Stress τ (MPa)\",\n    nameLocation: \"middle\",\n    nameGap: 56,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    min: -tauMax - pad,\n    max: tauMax + pad,\n    axisLabel: { color: t.inkSoft, fontSize: 14, formatter: (v) => Math.round(v) },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { show: true, lineStyle: { color: t.grid } },\n  },\n  series: [\n    {\n      // Mohr's circle outline, plus the two reference lines through the center\n      type: \"line\",\n      data: circlePoints,\n      symbol: \"none\",\n      lineStyle: { color: CLR_GEOM, width: 2.5 },\n      z: 3,\n      markLine: {\n        symbol: \"none\",\n        silent: true,\n        label: { show: false },\n        data: [\n          { yAxis: 0, lineStyle: { color: t.inkSoft, width: 1, type: \"solid\" } },\n          { xAxis: center, lineStyle: { color: t.inkSoft, width: 1, type: \"dashed\", opacity: 0.6 } },\n        ],\n      },\n    },\n    {\n      // Diameter line connecting stress points A and B\n      type: \"line\",\n      data: [\n        [sigmaX, tauXY],\n        [sigmaY, -tauXY],\n      ],\n      symbol: \"none\",\n      lineStyle: { color: CLR_INPUT, width: 1.5, type: \"dashed\", opacity: 0.55 },\n      z: 2,\n    },\n    {\n      // Principal-plane angle arc 2θp\n      type: \"line\",\n      data: arcPoints,\n      symbol: \"none\",\n      lineStyle: { color: CLR_GEOM, width: 2 },\n      z: 4,\n    },\n    {\n      // Angle label, offset past the arc tip — symbol kept tiny rather than\n      // \"none\" because a \"none\" symbol also suppresses its data-item label\n      type: \"scatter\",\n      symbol: \"circle\",\n      symbolSize: 2,\n      itemStyle: { color: CLR_GEOM },\n      data: [\n        {\n          value: arcLabelPos,\n          label: {\n            show: true,\n            formatter: `2θp = ${theta2p.toFixed(1)}°`,\n            color: CLR_GEOM,\n            fontSize: 14,\n            fontWeight: \"bold\",\n          },\n        },\n      ],\n      z: 4,\n    },\n    {\n      // Stress points A(σx, τxy) and B(σy, −τxy)\n      type: \"scatter\",\n      symbolSize: 16,\n      itemStyle: { color: CLR_INPUT, borderColor: t.pageBg, borderWidth: 2 },\n      data: [\n        {\n          value: [sigmaX, tauXY],\n          label: {\n            show: true,\n            formatter: `A (${sigmaX}, ${tauXY})`,\n            position: \"top\",\n            offset: [26, -16],\n            color: CLR_INPUT,\n            fontSize: 15,\n            fontWeight: \"bold\",\n          },\n        },\n        {\n          value: [sigmaY, -tauXY],\n          label: {\n            show: true,\n            formatter: `B (${sigmaY}, ${-tauXY})`,\n            position: \"bottom\",\n            offset: [-26, 16],\n            color: CLR_INPUT,\n            fontSize: 15,\n            fontWeight: \"bold\",\n          },\n        },\n      ],\n      z: 5,\n    },\n    {\n      // Principal stresses σ1, σ2 — diamond markers for derived quantities\n      type: \"scatter\",\n      symbol: \"diamond\",\n      itemStyle: { color: CLR_DERIVED, borderColor: t.pageBg, borderWidth: 2 },\n      data: [\n        {\n          value: [sigma2, 0],\n          symbolSize: 18,\n          label: {\n            show: true,\n            formatter: `σ₂ = ${sigma2.toFixed(1)} MPa`,\n            position: \"left\",\n            distance: 28,\n            offset: [0, -18],\n            color: CLR_DERIVED,\n            fontSize: 14,\n            fontWeight: \"bold\",\n          },\n        },\n        {\n          // σ1 is the critical engineering result — emphasized with a larger marker + callout\n          value: [sigma1, 0],\n          symbolSize: 24,\n          label: {\n            show: true,\n            formatter: `σ₁ = ${sigma1.toFixed(1)} MPa`,\n            position: \"right\",\n            distance: 32,\n            color: CLR_DERIVED,\n            fontSize: 15,\n            fontWeight: \"bold\",\n            backgroundColor: t.elevatedBg,\n            borderColor: CLR_DERIVED,\n            borderWidth: 1.2,\n            borderRadius: 4,\n            padding: [4, 8],\n          },\n        },\n      ],\n      z: 6,\n    },\n    {\n      // Maximum shear stress τ_max at top and bottom of the circle\n      type: \"scatter\",\n      symbol: \"diamond\",\n      symbolSize: 18,\n      itemStyle: { color: CLR_DERIVED, borderColor: t.pageBg, borderWidth: 2 },\n      data: [\n        {\n          value: [center, tauMax],\n          label: {\n            show: true,\n            formatter: `τ_max = ${tauMax.toFixed(1)} MPa`,\n            position: \"top\",\n            distance: 24,\n            color: CLR_DERIVED,\n            fontSize: 14,\n            fontWeight: \"bold\",\n          },\n        },\n        {\n          value: [center, -tauMax],\n          label: {\n            show: true,\n            formatter: `−τ_max = −${tauMax.toFixed(1)} MPa`,\n            position: \"bottom\",\n            distance: 24,\n            color: CLR_DERIVED,\n            fontSize: 14,\n            fontWeight: \"bold\",\n          },\n        },\n      ],\n      z: 6,\n    },\n    {\n      // Circle center C\n      type: \"scatter\",\n      symbol: \"circle\",\n      symbolSize: 10,\n      itemStyle: { color: CLR_GEOM, borderColor: t.pageBg, borderWidth: 1.5 },\n      data: [\n        {\n          value: [center, 0],\n          label: {\n            show: true,\n            formatter: `C (${center.toFixed(0)}, 0)`,\n            position: \"bottom\",\n            offset: [24, 26],\n            color: CLR_GEOM,\n            fontSize: 13,\n            fontWeight: \"bold\",\n          },\n        },\n      ],\n      z: 6,\n    },\n  ],\n});\n"}