fix(opticsbench): починка физики призмы — луч теперь правильно входит и преломляется

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
This commit is contained in:
Maxim Dolgolyov
2026-05-26 18:38:04 +03:00
parent 46d80c0bdf
commit ce54d3576d
+9 -5
View File
@@ -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;