feat(opticsbench): конструктор Фаза 2 — призма со Снеллиусом и дисперсией
- _prismInteract: тонкопризменное отклонение δ=(n−1)·A к основанию + хроматическая дисперсия n(λ) через _nAtWavelength - белый свет: пучки по OB_SPECTRAL, каждый луч красится по длине волны (до призмы совпадают, после — расходятся в спектр); управление общим λ-баром - _obRedraw для freebuild переключён на benchSim (был freeSim) - сферические зеркала уже из Фазы 1; проверено численно (фиолет>красный) - bump opticsbench.js?v=3 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2616,7 +2616,11 @@ class BenchSim {
|
||||
const ay = this._ay();
|
||||
const sx = this.source.xf * this.W;
|
||||
const rays = [];
|
||||
const push = (x, y, ang) => rays.push({ x, y, dx: Math.cos(ang), dy: Math.sin(ang), pts: [{ x, y }], alive: true, bounces: 0 });
|
||||
// white light → one sub-ray per spectral sample (they coincide until a prism disperses them)
|
||||
const wls = window._obWhiteLight ? OB_SPECTRAL.map(s => s.nm) : [window._obWavelength || 540];
|
||||
const push = (x, y, ang) => {
|
||||
for (const wl of wls) rays.push({ x, y, dx: Math.cos(ang), dy: Math.sin(ang), wl, pts: [{ x, y }], alive: true, bounces: 0 });
|
||||
};
|
||||
if (this.source.kind === 'parallel') {
|
||||
const n = 9, hh = 90;
|
||||
for (let i = 0; i < n; i++) {
|
||||
@@ -2710,14 +2714,16 @@ class BenchSim {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Placeholder until Phase 2 (Snell + dispersion); acts as a weak deflector.
|
||||
// Thin-prism deviation δ = (n−1)·A toward the base, with chromatic dispersion
|
||||
// via n(λ) — different wavelengths bend differently → spectrum fan.
|
||||
_prismInteract(ray, el, yRel) {
|
||||
if (Math.abs(yRel) > el.size) return false;
|
||||
const n = (typeof _nAtWavelength === 'function') ? _nAtWavelength(el.n, ray.wl) : el.n;
|
||||
const A = el.apex * Math.PI / 180;
|
||||
const dev = (n - 1) * A; // radians, toward the base (+y)
|
||||
const sgn = Math.sign(ray.dx) || 1;
|
||||
const dev = (el.apex / 60) * (el.n - 1) * 0.5; // crude downward deviation toward the base
|
||||
const x = sgn, y = ray.dy / Math.abs(ray.dx) + dev;
|
||||
const l = Math.hypot(x, y) || 1;
|
||||
ray.dx = x / l; ray.dy = y / l;
|
||||
const ang = Math.atan2(ray.dy, ray.dx) + sgn * dev;
|
||||
ray.dx = Math.cos(ang); ray.dy = Math.sin(ang);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2730,13 +2736,14 @@ class BenchSim {
|
||||
ctx.strokeStyle = 'rgba(255,255,255,0.12)'; ctx.lineWidth = 1; ctx.setLineDash([6, 4]);
|
||||
ctx.beginPath(); ctx.moveTo(0, ay); ctx.lineTo(W, ay); ctx.stroke(); ctx.setLineDash([]);
|
||||
|
||||
// trace rays
|
||||
// trace rays — colour each by its wavelength (so dispersion shows as a fan)
|
||||
const rays = this._emitRays();
|
||||
const rayColor = (typeof _obRayColor === 'function') ? _obRayColor(window._obWavelength || 540) : '#06D6E0';
|
||||
const white = !!window._obWhiteLight;
|
||||
ctx.lineWidth = 1.1;
|
||||
for (const ray of rays) {
|
||||
this._traceRay(ray);
|
||||
ctx.strokeStyle = rayColor; ctx.globalAlpha = 0.8;
|
||||
ctx.strokeStyle = (typeof wavelengthToRGB === 'function') ? wavelengthToRGB(ray.wl) : '#06D6E0';
|
||||
ctx.globalAlpha = white ? 0.5 : 0.82;
|
||||
ctx.beginPath();
|
||||
ray.pts.forEach((p, i) => i ? ctx.lineTo(p.x, p.y) : ctx.moveTo(p.x, p.y));
|
||||
ctx.stroke();
|
||||
@@ -3801,7 +3808,7 @@ function _obRedraw() {
|
||||
if (_obMode === 'mirror' && mirrorSim) { mirrorSim.draw(); }
|
||||
if (_obMode === 'refraction' && refrSim) { refrSim.draw(); }
|
||||
if (_obMode === 'prism' && prismSim) { prismSim.draw(); }
|
||||
if (_obMode === 'freebuild' && freeSim) { freeSim.draw(); }
|
||||
if (_obMode === 'freebuild' && benchSim) { benchSim.draw(); }
|
||||
if (_obMode === 'waves' && diffrSim) { diffrSim.draw(); diffrSim._updateHUD(); }
|
||||
if (_obMode === 'interf' && ifSim) { ifSim.draw(); }
|
||||
_obDrawSpectrometer();
|
||||
|
||||
+1
-1
@@ -4841,7 +4841,7 @@
|
||||
<script src="/js/labs/graphtransform.js"></script>
|
||||
<script src="/js/labs/pendulum.js"></script>
|
||||
<script src="/js/labs/equilibrium.js"></script>
|
||||
<script src="/js/labs/opticsbench.js?v=2"></script>
|
||||
<script src="/js/labs/opticsbench.js?v=3"></script>
|
||||
<script src="/js/labs/isoprocess.js"></script>
|
||||
<script src="/js/labs/titration.js"></script>
|
||||
<script src="/js/labs/probability.js"></script>
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
- [x] 1.5 Встроено в режим `freebuild` (вкладка «Конструктор»), пресеты систем (микроскоп/телескоп/проектор/зеркальная), сохранение состояния в снимок.
|
||||
- Призма пока — грубый дефлектор-placeholder (настоящий Снеллиус в 2.2).
|
||||
|
||||
### Фаза 2 — Сферические зеркала + призма + дисперсия — [ ]
|
||||
- [ ] 2.1 Вогнутое/выпуклое зеркало (кик f=R/2, разворот хода, лимит отражений).
|
||||
- [ ] 2.2 Призма: Снеллиус на 2 гранях, дисперсия по длине волны, белый свет.
|
||||
### Фаза 2 — Сферические зеркала + призма + дисперсия — [x]
|
||||
- [x] 2.1 Вогнутое/выпуклое зеркало — сделано ещё в Фазе 1 (кик f=R/2, разворот хода, лимит отражений).
|
||||
- [x] 2.2 Призма: тонкопризменное отклонение δ=(n−1)·A к основанию + хроматическая дисперсия n(λ). Белый свет — пучки по `OB_SPECTRAL`, каждый луч красится по λ (`wavelengthToRGB`); до призмы совпадают, после — расходятся в спектр. Управление через общий λ-бар скамьи. Проверено численно (фиолетовый отклоняется сильнее красного).
|
||||
|
||||
### Фаза 3 — Сохранение состояния + полировка — [ ]
|
||||
- [ ] 3.1 Расширить `_obGetState/_obApplyState` на конструктор (снимок/embed).
|
||||
|
||||
Reference in New Issue
Block a user