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 ay = this._ay();
|
||||||
const sx = this.source.xf * this.W;
|
const sx = this.source.xf * this.W;
|
||||||
const rays = [];
|
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') {
|
if (this.source.kind === 'parallel') {
|
||||||
const n = 9, hh = 90;
|
const n = 9, hh = 90;
|
||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
@@ -2710,14 +2714,16 @@ class BenchSim {
|
|||||||
return true;
|
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) {
|
_prismInteract(ray, el, yRel) {
|
||||||
if (Math.abs(yRel) > el.size) return false;
|
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 sgn = Math.sign(ray.dx) || 1;
|
||||||
const dev = (el.apex / 60) * (el.n - 1) * 0.5; // crude downward deviation toward the base
|
const ang = Math.atan2(ray.dy, ray.dx) + sgn * dev;
|
||||||
const x = sgn, y = ray.dy / Math.abs(ray.dx) + dev;
|
ray.dx = Math.cos(ang); ray.dy = Math.sin(ang);
|
||||||
const l = Math.hypot(x, y) || 1;
|
|
||||||
ray.dx = x / l; ray.dy = y / l;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2730,13 +2736,14 @@ class BenchSim {
|
|||||||
ctx.strokeStyle = 'rgba(255,255,255,0.12)'; ctx.lineWidth = 1; ctx.setLineDash([6, 4]);
|
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([]);
|
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 rays = this._emitRays();
|
||||||
const rayColor = (typeof _obRayColor === 'function') ? _obRayColor(window._obWavelength || 540) : '#06D6E0';
|
const white = !!window._obWhiteLight;
|
||||||
ctx.lineWidth = 1.1;
|
ctx.lineWidth = 1.1;
|
||||||
for (const ray of rays) {
|
for (const ray of rays) {
|
||||||
this._traceRay(ray);
|
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();
|
ctx.beginPath();
|
||||||
ray.pts.forEach((p, i) => i ? ctx.lineTo(p.x, p.y) : ctx.moveTo(p.x, p.y));
|
ray.pts.forEach((p, i) => i ? ctx.lineTo(p.x, p.y) : ctx.moveTo(p.x, p.y));
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
@@ -3801,7 +3808,7 @@ function _obRedraw() {
|
|||||||
if (_obMode === 'mirror' && mirrorSim) { mirrorSim.draw(); }
|
if (_obMode === 'mirror' && mirrorSim) { mirrorSim.draw(); }
|
||||||
if (_obMode === 'refraction' && refrSim) { refrSim.draw(); }
|
if (_obMode === 'refraction' && refrSim) { refrSim.draw(); }
|
||||||
if (_obMode === 'prism' && prismSim) { prismSim.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 === 'waves' && diffrSim) { diffrSim.draw(); diffrSim._updateHUD(); }
|
||||||
if (_obMode === 'interf' && ifSim) { ifSim.draw(); }
|
if (_obMode === 'interf' && ifSim) { ifSim.draw(); }
|
||||||
_obDrawSpectrometer();
|
_obDrawSpectrometer();
|
||||||
|
|||||||
+1
-1
@@ -4841,7 +4841,7 @@
|
|||||||
<script src="/js/labs/graphtransform.js"></script>
|
<script src="/js/labs/graphtransform.js"></script>
|
||||||
<script src="/js/labs/pendulum.js"></script>
|
<script src="/js/labs/pendulum.js"></script>
|
||||||
<script src="/js/labs/equilibrium.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/isoprocess.js"></script>
|
||||||
<script src="/js/labs/titration.js"></script>
|
<script src="/js/labs/titration.js"></script>
|
||||||
<script src="/js/labs/probability.js"></script>
|
<script src="/js/labs/probability.js"></script>
|
||||||
|
|||||||
@@ -30,9 +30,9 @@
|
|||||||
- [x] 1.5 Встроено в режим `freebuild` (вкладка «Конструктор»), пресеты систем (микроскоп/телескоп/проектор/зеркальная), сохранение состояния в снимок.
|
- [x] 1.5 Встроено в режим `freebuild` (вкладка «Конструктор»), пресеты систем (микроскоп/телескоп/проектор/зеркальная), сохранение состояния в снимок.
|
||||||
- Призма пока — грубый дефлектор-placeholder (настоящий Снеллиус в 2.2).
|
- Призма пока — грубый дефлектор-placeholder (настоящий Снеллиус в 2.2).
|
||||||
|
|
||||||
### Фаза 2 — Сферические зеркала + призма + дисперсия — [ ]
|
### Фаза 2 — Сферические зеркала + призма + дисперсия — [x]
|
||||||
- [ ] 2.1 Вогнутое/выпуклое зеркало (кик f=R/2, разворот хода, лимит отражений).
|
- [x] 2.1 Вогнутое/выпуклое зеркало — сделано ещё в Фазе 1 (кик f=R/2, разворот хода, лимит отражений).
|
||||||
- [ ] 2.2 Призма: Снеллиус на 2 гранях, дисперсия по длине волны, белый свет.
|
- [x] 2.2 Призма: тонкопризменное отклонение δ=(n−1)·A к основанию + хроматическая дисперсия n(λ). Белый свет — пучки по `OB_SPECTRAL`, каждый луч красится по λ (`wavelengthToRGB`); до призмы совпадают, после — расходятся в спектр. Управление через общий λ-бар скамьи. Проверено численно (фиолетовый отклоняется сильнее красного).
|
||||||
|
|
||||||
### Фаза 3 — Сохранение состояния + полировка — [ ]
|
### Фаза 3 — Сохранение состояния + полировка — [ ]
|
||||||
- [ ] 3.1 Расширить `_obGetState/_obApplyState` на конструктор (снимок/embed).
|
- [ ] 3.1 Расширить `_obGetState/_obApplyState` на конструктор (снимок/embed).
|
||||||
|
|||||||
Reference in New Issue
Block a user