'use strict'; const fs = require('fs'); const path = require('path'); const targetFile = path.join(__dirname, '../../frontend/js/labs/opticsbench.js'); let src = fs.readFileSync(targetFile, 'utf-8'); // --- 1. Add ifSim variable declaration after freeSim --- if (src.indexOf('var ifSim') < 0) { src = src.replace( 'var freeSim = null; /* multi-lens free-build (Agent OB-A3) */', 'var freeSim = null; /* multi-lens free-build (Agent OB-A3) */\r\nvar ifSim = null; /* interference/polarization (Agent C) */' ); console.log('Added ifSim declaration'); } else { console.log('ifSim declaration already exists'); } // --- 2. Add 'interf' to tab array in obSwitchMode --- const tabArrOld = "['lens', 'mirror', 'refraction', 'prism', 'freebuild', 'waves'].forEach(m => {"; const tabArrNew = "['lens', 'mirror', 'refraction', 'prism', 'freebuild', 'waves', 'interf'].forEach(m => {"; if (src.indexOf(tabArrNew) < 0) { if (src.indexOf(tabArrOld) >= 0) { src = src.replace(tabArrOld, tabArrNew); console.log('Updated tab array'); } else { console.log('WARN: tab array old pattern not found'); } } else { console.log('Tab array already updated'); } // --- 3. Add 'ob-ctrl-interf' to control panels array --- const ctrlArrOld = "['ob-ctrl-lens', 'ob-ctrl-mirror', 'ob-ctrl-refraction', 'ob-ctrl-prism', 'ob-ctrl-freebuild', 'ob-ctrl-waves'].forEach(id => {"; const ctrlArrNew = "['ob-ctrl-lens', 'ob-ctrl-mirror', 'ob-ctrl-refraction', 'ob-ctrl-prism', 'ob-ctrl-freebuild', 'ob-ctrl-waves', 'ob-ctrl-interf'].forEach(id => {"; if (src.indexOf(ctrlArrNew) < 0) { if (src.indexOf(ctrlArrOld) >= 0) { src = src.replace(ctrlArrOld, ctrlArrNew); console.log('Updated ctrl panel array'); } else { console.log('WARN: ctrl panel array old pattern not found'); } } else { console.log('Ctrl panel array already updated'); } // --- 4. Add 'ob-stats-interf' to stats array --- const statsArrOld = "['ob-stats-lens', 'ob-stats-mirror', 'ob-stats-refr', 'ob-stats-prism', 'ob-stats-freebuild', 'ob-stats-waves'].forEach(id => {"; const statsArrNew = "['ob-stats-lens', 'ob-stats-mirror', 'ob-stats-refr', 'ob-stats-prism', 'ob-stats-freebuild', 'ob-stats-waves', 'ob-stats-interf'].forEach(id => {"; if (src.indexOf(statsArrNew) < 0) { if (src.indexOf(statsArrOld) >= 0) { src = src.replace(statsArrOld, statsArrNew); console.log('Updated stats array'); } else { console.log('WARN: stats array old pattern not found'); } } else { console.log('Stats array already updated'); } // --- 5. Add 'ob-interf-canvas' to canvas arrays --- const canvasIdsOld = "const canvasIds = ['ob-lens-canvas', 'ob-mirror-canvas', 'ob-refr-canvas', 'ob-prism-canvas', 'ob-free-canvas', 'ob-waves-canvas'];"; const canvasIdsNew = "const canvasIds = ['ob-lens-canvas', 'ob-mirror-canvas', 'ob-refr-canvas', 'ob-prism-canvas', 'ob-free-canvas', 'ob-waves-canvas', 'ob-interf-canvas'];"; if (src.indexOf(canvasIdsNew) < 0) { if (src.indexOf(canvasIdsOld) >= 0) { src = src.replace(canvasIdsOld, canvasIdsNew); console.log('Updated canvasIds'); } else { console.log('WARN: canvasIds old pattern not found'); } } else { console.log('canvasIds already updated'); } const modeCanvasOld = "const modeCanvas = { lens: 'ob-lens-canvas', mirror: 'ob-mirror-canvas', refraction: 'ob-refr-canvas', prism: 'ob-prism-canvas', freebuild: 'ob-free-canvas', waves: 'ob-waves-canvas' };"; const modeCanvasNew = "const modeCanvas = { lens: 'ob-lens-canvas', mirror: 'ob-mirror-canvas', refraction: 'ob-refr-canvas', prism: 'ob-prism-canvas', freebuild: 'ob-free-canvas', waves: 'ob-waves-canvas', interf: 'ob-interf-canvas' };"; if (src.indexOf(modeCanvasNew) < 0) { if (src.indexOf(modeCanvasOld) >= 0) { src = src.replace(modeCanvasOld, modeCanvasNew); console.log('Updated modeCanvas'); } else { console.log('WARN: modeCanvas old pattern not found'); } } else { console.log('modeCanvas already updated'); } // --- 6. Add 'interf' case in obSwitchMode, before closing brace --- const interfCaseStr = ` } else if (mode === 'interf') { /* Agent C — interference / polarization */\r\n if (!ifSim) {\r\n const cv = document.getElementById('ob-interf-canvas');\r\n if (cv) { ifSim = new InterferenceSim(cv); ifSim.onUpdate = _ifUpdateUI; }\r\n }\r\n if (ifSim) { ifSim.fit(); ifSim.draw(); }\r\n _ifUpdateUI();\r\n }\r\n}`; const oldModeEnd = ` } else if (mode === 'waves') { /* Agent B1 — diffraction & interference */\r\n if (!diffrSim) {\r\n const cv = document.getElementById('ob-waves-canvas');\r\n if (cv) diffrSim = new DiffractionSim(cv);\r\n }\r\n if (diffrSim) {\r\n diffrSim.fit();\r\n diffrSim.draw();\r\n diffrSim._updateHUD();\r\n }\r\n }\r\n}`; const newModeEnd = ` } else if (mode === 'waves') { /* Agent B1 — diffraction & interference */\r\n if (!diffrSim) {\r\n const cv = document.getElementById('ob-waves-canvas');\r\n if (cv) diffrSim = new DiffractionSim(cv);\r\n }\r\n if (diffrSim) {\r\n diffrSim.fit();\r\n diffrSim.draw();\r\n diffrSim._updateHUD();\r\n }\r\n } else if (mode === 'interf') { /* Agent C — interference / polarization */\r\n if (!ifSim) {\r\n const cv = document.getElementById('ob-interf-canvas');\r\n if (cv) { ifSim = new InterferenceSim(cv); ifSim.onUpdate = _ifUpdateUI; }\r\n }\r\n if (ifSim) { ifSim.fit(); ifSim.draw(); }\r\n _ifUpdateUI();\r\n }\r\n}`; if (src.indexOf('else if (mode === \'interf\')') < 0) { if (src.indexOf(oldModeEnd) >= 0) { src = src.replace(oldModeEnd, newModeEnd); console.log('Added interf case in obSwitchMode'); } else { // Try with LF only const oldLF = oldModeEnd.replace(/\r\n/g, '\n'); const newLF = newModeEnd.replace(/\r\n/g, '\n'); if (src.indexOf(oldLF) >= 0) { src = src.replace(oldLF, newLF); console.log('Added interf case (LF variant)'); } else { console.log('WARN: waves mode end pattern not found - trying fallback'); // Fallback: find closing brace of obSwitchMode after diffrSim block const marker2 = "diffrSim._updateHUD();\n }\n }\n}"; const marker2cr = "diffrSim._updateHUD();\r\n }\r\n }\r\n}"; const repl2 = "diffrSim._updateHUD();\n }\n } else if (mode === 'interf') {\n if (!ifSim) {\n const cv = document.getElementById('ob-interf-canvas');\n if (cv) { ifSim = new InterferenceSim(cv); ifSim.onUpdate = _ifUpdateUI; }\n }\n if (ifSim) { ifSim.fit(); ifSim.draw(); }\n _ifUpdateUI();\n }\n}"; const repl2cr = "diffrSim._updateHUD();\r\n }\r\n } else if (mode === 'interf') {\r\n if (!ifSim) {\r\n const cv = document.getElementById('ob-interf-canvas');\r\n if (cv) { ifSim = new InterferenceSim(cv); ifSim.onUpdate = _ifUpdateUI; }\r\n }\r\n if (ifSim) { ifSim.fit(); ifSim.draw(); }\r\n _ifUpdateUI();\r\n }\r\n}"; if (src.indexOf(marker2) >= 0) { src = src.replace(marker2, repl2); console.log('Added interf case (fallback LF)'); } else if (src.indexOf(marker2cr) >= 0) { src = src.replace(marker2cr, repl2cr); console.log('Added interf case (fallback CRLF)'); } else { console.log('WARN: all fallbacks failed for interf case'); } } } } else { console.log('interf case already exists'); } // --- 7. Add _ifUpdateUI function and control functions --- const ifUICode = ` /* ── Interference mode UI callbacks (Agent C) ── */ function _ifUpdateUI() { if (!ifSim) return; const subMode = ifSim.subMode; ['if-ctrl-newton', 'if-ctrl-thinfilm', 'if-ctrl-polarization'].forEach(id => { const el = document.getElementById(id); if (el) el.style.display = 'none'; }); const active = document.getElementById('if-ctrl-' + subMode); if (active) active.style.display = ''; ['if-sub-newton', 'if-sub-thinfilm', 'if-sub-polarization'].forEach(id => { const el = document.getElementById(id); if (el) el.classList.toggle('active', id === 'if-sub-' + subMode); }); } function ifSwitchSub(sub) { if (window.LabFX) LabFX.sound.play('chime'); if (!ifSim) return; ifSim.setSubMode(sub); _ifUpdateUI(); } function ifNewtParam(key, val) { if (!ifSim) return; const v = parseFloat(val); if (key === 'R') { ifSim.nR = v; document.getElementById('if-newton-r-val').textContent = v; } else if (key === 'nmax') { ifSim.nNmax = Math.round(v); document.getElementById('if-newton-n-val').textContent = Math.round(v); } ifSim.draw(); } function ifThinFilmParam(key, val) { if (!ifSim) return; const v = parseFloat(val); if (key === 't') { ifSim.tfT = v; document.getElementById('if-tf-t-val').textContent = v; } else if (key === 'n') { ifSim.tfN = v; document.getElementById('if-tf-n-val').textContent = v.toFixed(2); } else if (key === 'theta') { ifSim.tfTheta = v; document.getElementById('if-tf-th-val').textContent = v; } ifSim.draw(); } function ifThinFilmPreset(name) { if (!ifSim) return; const presets = { soap: { n: 1.33, label: 'Мыльная плёнка' }, oil: { n: 1.50, label: 'Масло на воде' }, coating: { n: 1.38, label: 'Антибликовое покрытие' }, }; const p = presets[name]; if (!p) return; ifSim.tfN = p.n; ifSim.tfPreset = name; const slN = document.getElementById('sl-if-tf-n'); if (slN) slN.value = p.n; const lbN = document.getElementById('if-tf-n-val'); if (lbN) lbN.textContent = p.n.toFixed(2); ifSim.draw(); if (window.LabFX) LabFX.sound.play('chime'); } function ifPolParam(key, val) { if (!ifSim) return; const v = parseFloat(val); if (key === 'theta') { ifSim.polTheta = v; document.getElementById('if-pol-th-val').textContent = v; } ifSim.draw(); } function ifPolSrc(val) { if (!ifSim) return; ifSim.polSrc = val; ifSim.draw(); } `; if (src.indexOf('function _ifUpdateUI') < 0) { // Insert before the closing of the file or before _obRedraw const insertBefore = 'function _obRedraw()'; const insertIdx = src.indexOf(insertBefore); if (insertIdx >= 0) { src = src.slice(0, insertIdx) + ifUICode + '\r\n' + src.slice(insertIdx); console.log('Added _ifUpdateUI and control functions'); } else { src = src + '\r\n' + ifUICode; console.log('Appended _ifUpdateUI at end'); } } else { console.log('_ifUpdateUI already exists'); } // --- 8. Make _obRedraw also redraw ifSim if active --- const redrawOld = 'function _obRedraw() {'; const redrawNew = 'function _obRedraw() {'; // Find _obRedraw body and add ifSim redraw if (src.indexOf('if (ifSim && _obMode') < 0) { const marker3 = "if (prismSim && _obMode === 'prism')"; const repl3 = "if (ifSim && _obMode === 'interf') { ifSim.draw(); }\r\n if (prismSim && _obMode === 'prism')"; const marker3lf = "if (prismSim && _obMode === 'prism')"; if (src.indexOf(marker3) >= 0) { src = src.replace(marker3, repl3); console.log('Added ifSim redraw to _obRedraw'); } else { console.log('WARN: could not find _obRedraw prism marker'); } } fs.writeFileSync(targetFile, src, 'utf-8'); console.log('Done. New size:', src.length);