{"spec_id":"mohr-circle","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// mohr-circle: Mohr's Circle for Stress Analysis\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-08-26\n\n//# anyplot-orientation: square\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: 2D stress state on a steel shaft under combined bending + torsion (MPa) ---\nconst sigmaX = 120;\nconst sigmaY = 40;\nconst tauXY = 55;\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 twoThetaPRad = Math.atan2(tauXY, (sigmaX - sigmaY) / 2);\nconst twoThetaPDeg = (twoThetaPRad * 180) / Math.PI;\n\n// Circle outline, traced parametrically around the center\nconst STEPS = 200;\nconst circlePoints = [];\nfor (let i = 0; i <= STEPS; i++) {\n  const angle = (2 * Math.PI * i) / STEPS;\n  circlePoints.push([center + radius * Math.cos(angle), radius * Math.sin(angle)]);\n}\n\nconst pointA = [sigmaX, tauXY];\nconst pointB = [sigmaY, -tauXY];\n\n// --- Chart -------------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"line\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n    events: {\n      load: function () {\n        // Force equal pixel-per-unit scale on both axes so the circle renders\n        // as a true circle instead of stretching to the plot area's aspect ratio.\n        const chart = this;\n        const xAxis = chart.xAxis[0];\n        const yAxis = chart.yAxis[0];\n        const scale = Math.min(chart.plotWidth / (xAxis.max - xAxis.min), chart.plotHeight / (yAxis.max - yAxis.min));\n        const xMid = (xAxis.max + xAxis.min) / 2;\n        const yMid = (yAxis.max + yAxis.min) / 2;\n        xAxis.setExtremes(xMid - chart.plotWidth / (2 * scale), xMid + chart.plotWidth / (2 * scale), false);\n        yAxis.setExtremes(yMid - chart.plotHeight / (2 * scale), yMid + chart.plotHeight / (2 * scale), false);\n        chart.redraw();\n\n        // Angle arc for the principal-plane rotation 2θp, swept from the\n        // positive-σ direction (toward σ1) to the radius line center → A.\n        const cx = xAxis.toPixels(center);\n        const cy = yAxis.toPixels(0);\n        const ax = xAxis.toPixels(pointA[0]);\n        const ay = yAxis.toPixels(pointA[1]);\n        const arcR = Math.min(chart.plotWidth, chart.plotHeight) * 0.14;\n        // Highcharts renderer.arc angles: 0 rad = 3 o'clock (right), increasing clockwise.\n        const angleToA = Math.atan2(ay - cy, ax - cx);\n        const startAngle = Math.min(0, angleToA);\n        const endAngle = Math.max(0, angleToA);\n        chart.renderer\n          .arc(cx, cy, arcR, 0, startAngle, endAngle)\n          .attr({ stroke: t.inkSoft, \"stroke-width\": 1.5, fill: \"none\", dashstyle: \"Dash\" })\n          .add();\n        const midAngle = (startAngle + endAngle) / 2;\n        const labelR = arcR + 34;\n        // Use renderer.label (not .text) so the halo matching the page background\n        // sits behind the glyphs, keeping the dashed arc from cutting through them.\n        const angleLabel = chart.renderer\n          .label(`2θp = ${twoThetaPDeg.toFixed(1)}°`, 0, 0)\n          .attr({ fill: t.pageBg, padding: 3, zIndex: 6 })\n          .css({ color: t.inkSoft, fontSize: \"14px\" })\n          .add();\n        const labelBox = angleLabel.getBBox();\n        angleLabel.attr({\n          x: cx + labelR * Math.cos(midAngle) - labelBox.width / 2,\n          y: cy + labelR * Math.sin(midAngle) - labelBox.height / 2,\n        });\n      },\n    },\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"mohr-circle · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  subtitle: {\n    text: \"Steel shaft under combined bending and torsion\",\n    style: { color: t.inkSoft, fontSize: \"14px\" },\n  },\n  xAxis: {\n    title: { text: \"Normal Stress σ (MPa)\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    min: center - radius * 1.3,\n    max: center + radius * 1.3,\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    gridLineWidth: 1,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    plotLines: [\n      { value: 0, color: t.inkSoft, width: 1, zIndex: 2 },\n      { value: center, color: t.inkSoft, width: 1, dashStyle: \"ShortDash\", zIndex: 2 },\n    ],\n  },\n  yAxis: {\n    title: { text: \"Shear Stress τ (MPa)\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    min: -radius * 1.3,\n    max: radius * 1.3,\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    gridLineWidth: 1,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    plotLines: [{ value: 0, color: t.inkSoft, width: 1, zIndex: 2 }],\n  },\n  legend: { enabled: false },\n  tooltip: {\n    pointFormat: \"σ = {point.x:.1f} MPa<br/>τ = {point.y:.1f} MPa\",\n  },\n  plotOptions: {\n    series: { animation: false },\n  },\n  series: [\n    {\n      name: \"Mohr's Circle\",\n      type: \"line\",\n      data: circlePoints,\n      color: t.palette[0],\n      lineWidth: 3,\n      marker: { enabled: false },\n      enableMouseTracking: false,\n    },\n    {\n      name: \"Diameter (A-B)\",\n      type: \"line\",\n      data: [pointA, pointB],\n      color: t.inkSoft,\n      dashStyle: \"ShortDash\",\n      lineWidth: 1.5,\n      marker: { enabled: false },\n      enableMouseTracking: false,\n    },\n    {\n      name: \"Stress Points\",\n      type: \"scatter\",\n      color: t.palette[1],\n      marker: { symbol: \"circle\", radius: 6, lineWidth: 1, lineColor: t.pageBg },\n      dataLabels: {\n        enabled: true,\n        style: { color: t.ink, fontSize: \"14px\", textOutline: \"none\" },\n        formatter: function () {\n          return this.point.label;\n        },\n      },\n      data: [\n        { x: pointA[0], y: pointA[1], label: `A(${sigmaX}, ${tauXY})`, dataLabels: { align: \"left\", x: 10, y: -8 } },\n        { x: pointB[0], y: pointB[1], label: `B(${sigmaY}, -${tauXY})`, dataLabels: { align: \"right\", x: -10, y: 18 } },\n      ],\n    },\n    {\n      name: \"Principal Stresses\",\n      type: \"scatter\",\n      color: t.palette[2],\n      marker: { symbol: \"diamond\", radius: 7, lineWidth: 1, lineColor: t.pageBg },\n      dataLabels: {\n        enabled: true,\n        style: { color: t.ink, fontSize: \"14px\", textOutline: \"none\" },\n        backgroundColor: t.pageBg,\n        borderWidth: 0,\n        borderRadius: 3,\n        padding: 3,\n        formatter: function () {\n          return this.point.label;\n        },\n      },\n      // σ1/σ2 sit at the circle's extreme left/right points, where the tangent\n      // is near-vertical — push the labels outward (away from the circle body)\n      // instead of straight up, so the stroke doesn't cut through the text.\n      data: [\n        { x: sigma1, y: 0, label: `σ1 = ${sigma1.toFixed(1)}`, dataLabels: { align: \"left\", x: 12, y: -10 } },\n        { x: sigma2, y: 0, label: `σ2 = ${sigma2.toFixed(1)}`, dataLabels: { align: \"right\", x: -12, y: -10 } },\n      ],\n    },\n    {\n      name: \"Max Shear Stress\",\n      type: \"scatter\",\n      color: t.palette[3],\n      marker: { symbol: \"triangle\", radius: 7, lineWidth: 1, lineColor: t.pageBg },\n      dataLabels: {\n        enabled: true,\n        style: { color: t.ink, fontSize: \"14px\", textOutline: \"none\" },\n        backgroundColor: t.pageBg,\n        borderWidth: 0,\n        borderRadius: 3,\n        padding: 3,\n        formatter: function () {\n          return this.point.label;\n        },\n      },\n      // τmax/-τmax sit on the dashed σ=center reference line — offset the\n      // labels sideways (not centered on x=center) so the dashed line doesn't\n      // run straight through the text.\n      data: [\n        { x: center, y: tauMax, label: `τmax = ${tauMax.toFixed(1)}`, dataLabels: { align: \"left\", x: 10, y: -10 } },\n        { x: center, y: -tauMax, label: `-τmax`, dataLabels: { align: \"left\", x: 10, y: 22 } },\n      ],\n    },\n    {\n      name: \"Center\",\n      type: \"scatter\",\n      color: t.ink,\n      marker: { symbol: \"circle\", radius: 4 },\n      enableMouseTracking: false,\n      showInLegend: false,\n      dataLabels: { enabled: false },\n      data: [{ x: center, y: 0 }],\n    },\n  ],\n});\n"}