diff --git a/frontend/js/labs/opticsbench.js b/frontend/js/labs/opticsbench.js index e97cb92..f77db02 100644 --- a/frontend/js/labs/opticsbench.js +++ b/frontend/js/labs/opticsbench.js @@ -2705,7 +2705,7 @@ class BenchSim { return true; } if (el.type === 'screen') { - ray.hitY = ray.y; ray.alive = false; // absorbed, hit recorded + ray.hitY = ray.y; ray.hitEl = el.id; ray.alive = false; // absorbed, hit recorded return true; } if (el.type === 'prism') { @@ -2754,11 +2754,34 @@ class BenchSim { this._drawSource(ctx, ay); for (const el of this.elements) this._drawElement(ctx, el, ay); + // image formed on screens (where rays land) + this._drawScreenHits(ctx, rays); + if (typeof _drawOBFXLayer === 'function') { _drawOBFXLayer(ctx, 'freebuild', { srcX: this.source.xf * W, srcY: ay - (this.source.h || 0) }); } } + // Glowing spots on each screen where rays land → the image becomes visible. + _drawScreenHits(ctx, rays) { + const screens = this.elements.filter(e => e.type === 'screen'); + if (!screens.length) return; + ctx.save(); + ctx.globalCompositeOperation = 'lighter'; // additive → overlapping rays brighten + for (const sc of screens) { + const x = this._ex(sc); + for (const r of rays) { + if (r.hitEl !== sc.id) continue; + const col = (typeof wavelengthToRGB === 'function') ? wavelengthToRGB(r.wl) : '#fff'; + const g = ctx.createRadialGradient(x, r.hitY, 0, x, r.hitY, 9); + g.addColorStop(0, col); g.addColorStop(1, 'rgba(0,0,0,0)'); + ctx.fillStyle = g; ctx.globalAlpha = 0.5; + ctx.beginPath(); ctx.arc(x, r.hitY, 9, 0, Math.PI * 2); ctx.fill(); + } + } + ctx.restore(); + } + _drawSource(ctx, ay) { const sx = this.source.xf * this.W; ctx.save(); @@ -2880,6 +2903,10 @@ class BenchSim { on(cv, 'pointerup', e => { this._drag = null; try { cv.releasePointerCapture(e.pointerId); } catch (_) {} }); } + exportPng() { + try { return this.canvas.toDataURL('image/png'); } catch (_) { return null; } + } + dispose() { if (this._ro) { this._ro.disconnect(); this._ro = null; } if (this._listeners) { for (const [t, ty, fn, o] of this._listeners) t.removeEventListener(ty, fn, o); this._listeners = []; } @@ -4404,6 +4431,14 @@ function benchClear() { if (!benchSim) return; benchSim.elements = []; benchSim.selectedId = null; benchSim._changed(); _benchUpdateUI(); } +function benchExportPng() { + if (!benchSim) return; + const url = benchSim.exportPng(); + if (!url) return; + const a = document.createElement('a'); + a.href = url; a.download = 'optical-bench.png'; + document.body.appendChild(a); a.click(); document.body.removeChild(a); +} function benchPreset(name) { if (!benchSim) return; const P = { diff --git a/frontend/lab.html b/frontend/lab.html index 0249e02..f1eaf0e 100644 --- a/frontend/lab.html +++ b/frontend/lab.html @@ -3205,8 +3205,11 @@ - -
Тащи элементы и источник по оси. Клик — выбрать и настроить.
+
+ + +
+
Тащи элементы и источник по оси. Клик — выбрать и настроить. λ и «Белый свет» — сверху.