From ce54d3576dc8519563ffbf354c5023cfdee117d1 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Tue, 26 May 2026 18:38:04 +0300 Subject: [PATCH] =?UTF-8?q?fix(opticsbench):=20=D0=BF=D0=BE=D1=87=D0=B8?= =?UTF-8?q?=D0=BD=D0=BA=D0=B0=20=D1=84=D0=B8=D0=B7=D0=B8=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=D0=B7=D0=BC=D1=8B=20=E2=80=94=20=D0=BB=D1=83?= =?UTF-8?q?=D1=87=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D0=BB=D1=8C=D0=BD=D0=BE=20=D0=B2=D1=85=D0=BE?= =?UTF-8?q?=D0=B4=D0=B8=D1=82=20=D0=B8=20=D0=BF=D1=80=D0=B5=D0=BB=D0=BE?= =?UTF-8?q?=D0=BC=D0=BB=D1=8F=D0=B5=D1=82=D1=81=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PrismSim был сломан в 3 местах: 1. incDir строился с -efNorm (наружу), а не efNorm (внутрь) → падающий луч рисовался не с той стороны 2. cosI = -(incDir·efNorm) с уже-перевёрнутым incDir давал противоречивые знаки 3. Формула Снелла rDir имела + вместо - на коэффициенте efNorm Итог: при incAngle≈0 преломлённый луч уходил в обратную сторону, точка пересечения с выходной гранью не находилась (tRay<0), и наружный луч с дисперсией не отрисовывался → визуально 'призма не работает'. Теперь incDir — направление распространения (внутрь призмы), cosI = +(incDir·efNorm), формула: r = (1/n)·l + (cosR − cosI/n)·n --- frontend/js/labs/opticsbench.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/frontend/js/labs/opticsbench.js b/frontend/js/labs/opticsbench.js index 7a8b258..0181e1b 100644 --- a/frontend/js/labs/opticsbench.js +++ b/frontend/js/labs/opticsbench.js @@ -2616,14 +2616,16 @@ class PrismSim { const eX = (v[0].x + v[2].x) / 2, eY = (v[0].y + v[2].y) / 2; const tangDir = { x: efVec.x / efLen, y: efVec.y / efLen }; const incRad = this.incAngle * Math.PI / 180; + /* incDir = propagation direction (INTO the prism) */ const incDir = { - x: Math.cos(incRad) * (-efNorm.x) + Math.sin(incRad) * tangDir.x, - y: Math.cos(incRad) * (-efNorm.y) + Math.sin(incRad) * tangDir.y, + x: Math.cos(incRad) * efNorm.x + Math.sin(incRad) * tangDir.x, + y: Math.cos(incRad) * efNorm.y + Math.sin(incRad) * tangDir.y, }; const incDLen = Math.hypot(incDir.x, incDir.y); incDir.x /= incDLen; incDir.y /= incDLen; const rayLen = W * 0.45; + /* draw incoming ray from outside (source side) to entry midpoint */ this._drawRayLine(ctx, eX - incDir.x * rayLen, eY - incDir.y * rayLen, eX, eY, isWhite ? '#FFFFFF' : wavelengthToRGB(monoNm), 2.5); @@ -2639,13 +2641,15 @@ class PrismSim { for (const s of spectral) { const nP = _nAtWavelength(this.n0, s.nm); const col = wavelengthToRGB(s.nm); - const cosI = -(incDir.x * efNorm.x + incDir.y * efNorm.y); + /* Snell vector form: l = incDir (propagation), n = -efNorm (toward incident medium) + cosθ_i = -n·l = efNorm·l; r = μl + (μcosθ_i - cosθ_t)·n */ + const cosI = (incDir.x * efNorm.x + incDir.y * efNorm.y); const sinR = Math.sqrt(Math.max(0, 1 - cosI * cosI)) / nP; if (sinR > 1) continue; const cosR = Math.sqrt(1 - sinR * sinR); const rDir = { - x: (1/nP) * incDir.x + ((1/nP) * cosI - cosR) * efNorm.x, - y: (1/nP) * incDir.y + ((1/nP) * cosI - cosR) * efNorm.y, + x: (1/nP) * incDir.x + (cosR - (1/nP) * cosI) * efNorm.x, + y: (1/nP) * incDir.y + (cosR - (1/nP) * cosI) * efNorm.y, }; const rDLen = Math.hypot(rDir.x, rDir.y); rDir.x /= rDLen; rDir.y /= rDLen;