From a6bc034bdb4e61df6bc03ef8f43d8c3a0bb44590 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Sat, 30 May 2026 08:58:32 +0300 Subject: [PATCH] =?UTF-8?q?feat(phys8=20ch3):=20Phase=205=20Wave=203+4=20?= =?UTF-8?q?=E2=80=94=20=C2=A736-40=20+=20=D0=A4=D0=B8=D0=BD=D0=B0=D0=BB=20?= =?UTF-8?q?=E2=80=94=20=D0=93=D0=BB=D0=B0=D0=B2=D0=B0=203=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D1=88=D0=B5=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §36 Преломление света: - Закон Снеллиуса с интерактивным OPTICS.refractRay - 4 материала (воздух/вода/стекло/алмаз) - Полное внутреннее отражение - 5 численных задач §37 Линзы. Оптическая сила: - OPTICS.thinLens — собирающая и рассеивающая - D = 1/F, дптр - 5 задач (включая F=17 мм для глаза) §38 Построение изображений (ГЛАВНЫЙ ВИЗУАЛ ОПТИКИ): - Конструктор изображения через OPTICS.buildLensImage - slider F и d, увеличение, тип изображения - 5 типов (d>2F/2F/F --- frontend/textbooks/physics_8_ch3.html | 912 +++++++++++++++++++++++++- 1 file changed, 901 insertions(+), 11 deletions(-) diff --git a/frontend/textbooks/physics_8_ch3.html b/frontend/textbooks/physics_8_ch3.html index 642f206..8107502 100644 --- a/frontend/textbooks/physics_8_ch3.html +++ b/frontend/textbooks/physics_8_ch3.html @@ -1264,17 +1264,907 @@ function _p35_tasks(){ r(); } -/* === Заглушки для §36..§40 и финала — будут заполнены ниже === */ -function build_p36(){ _stub_phaseN('p36','Преломление света','Phase 5 Wave 3'); } -function build_p37(){ _stub_phaseN('p37','Линзы. Оптическая сила','Phase 5 Wave 3'); } -function build_p38(){ _stub_phaseN('p38','Построение изображений в линзах','Phase 5 Wave 4'); } -function build_p39(){ _stub_phaseN('p39','Глаз как оптическая система','Phase 5 Wave 4'); } -function build_p40(){ _stub_phaseN('p40','Дефекты зрения. Очки','Phase 5 Wave 4'); } -function build_final3(){ _stub_phaseN('final3','Финал главы 3','Phase 5 Wave 4'); } -function _stub_phaseN(id, name, ph){ - const box = document.getElementById(id+'-body'); - box.innerHTML = buildStub(id, name, ph) + secNavFor(id) + readButton(id); - renderMath(box); wireReadBtn(id); +/* ======== §36 — Преломление света ======== */ +function build_p36(){ + const box = document.getElementById('p36-body'); let h = ''; + h += makeCard('theory', 'Закон преломления', '§ 36.1', + '

При переходе из одной среды в другую луч света меняет направление. Это преломление.

' + +'

Закон Снеллиуса:

' + +'

$$\\dfrac{\\sin\\alpha}{\\sin\\beta} = \\dfrac{n_2}{n_1} = n_{2/1}$$

' + +'

$n$ — показатель преломления среды. Чем больше $n$, тем сильнее замедляется свет.

' + ); + h += makeCard('rule', 'Когда $\\alpha > \\beta$, а когда наоборот', '§ 36.2', + '' + +'

При большом $\\alpha$ из плотной среды возможно полное внутреннее отражение: луч вообще не выходит.

' + +'

Таблица $n$:

' + +'' + ); + h += makeCard('example', 'Примеры', '§ 36.3', + '' + ); + h += '
IV-1
Закон Снеллиуса
' + +'
Меняй $\\alpha$ и $n$ — увидь, как меняется $\\beta$. При большом $\\alpha$ из плотной среды наступает полное внутреннее отражение.
' + +'
' + +'' + +'
' + +'' + +'
$\\beta$ = 22°
'; + h += '
IV-2
Преломление: больше / меньше
' + +'
При переходе в указанную среду угол $\\alpha$ — больше или меньше $\\beta$?
' + +'
' + +'
' + +'
Раунд: 1/5Правильно: 0
'; + h += '
IV-3
Расставь по возрастанию $n$
' + +'
' + +'
1
2
3
4
' + +'
' + +'
'; + h += '
IV-4
Тренажёр: 5 задач
' + +'
4+ — +15 XP.
' + +'
' + +'
Задача: 1/5Правильно: 0
'; + box.innerHTML = h + secNavFor('p36') + readButton('p36'); + renderMath(box); wireReadBtn('p36'); + _p36_snell(); _p36_quiz(); _p36_dnd(); _p36_tasks(); +} +function _p36_snell(){ + const svg = document.getElementById('p36-sim'); if(!svg) return; + function d(){ + const a = +document.getElementById('p36-a').value; + const n1 = +document.getElementById('p36-n1').value; + const n2 = +document.getElementById('p36-n2').value; + document.getElementById('p36-av').textContent = a; + const sinB = (n1/n2) * Math.sin(a*Math.PI/180); + const tir = Math.abs(sinB) > 1; + document.getElementById('p36-tir-lab').style.display = tir ? 'inline' : 'none'; + if(tir){ + document.getElementById('p36-bv').textContent = '—'; + } else { + const b = Math.asin(sinB) * 180/Math.PI; + document.getElementById('p36-bv').textContent = b.toFixed(1); + } + const result = window.OPTICS.refractRay(230, 140, a, n1, n2, 90); + let bg = ''; + /* верхняя среда */ + bg += ''; + /* нижняя среда */ + bg += ''; + bg += 'n₁ = '+n1+''; + bg += 'n₂ = '+n2+''; + svg.innerHTML = bg + result.svg; + } + ['p36-a','p36-n1','p36-n2'].forEach(id => document.getElementById(id).addEventListener('input', d)); + document.getElementById('p36-n1').addEventListener('change', d); + document.getElementById('p36-n2').addEventListener('change', d); + d(); +} +function _p36_quiz(){ + const QS = [ + {sit:'воздух → вода', ans:'L', why:'В более плотную: $\\alpha > \\beta$, луч ближе к нормали.'}, + {sit:'вода → воздух', ans:'B', why:'Из более плотной: $\\alpha < \\beta$.'}, + {sit:'воздух → стекло', ans:'L', why:'$\\alpha > \\beta$.'}, + {sit:'стекло → воздух', ans:'B', why:'$\\alpha < \\beta$.'}, + {sit:'воздух → алмаз', ans:'L', why:'$n_{алмаза} = 2{,}42$, очень плотный — $\\alpha \\gg \\beta$.'} + ]; + let i = 0, ok = 0; + function r(){ + const q = QS[i]; const w = document.getElementById('p36-quiz'); + w.innerHTML = '
'+q.sit+'
' + +'
' + +''; + document.getElementById('p36-quiz-r').textContent = (i+1); + document.getElementById('p36-quiz-ok').textContent = ok; + w.querySelectorAll('[data-p]').forEach(b=>{ + b.addEventListener('click', ()=>{ + if(b.disabled) return; w.querySelectorAll('[data-p]').forEach(x=>x.disabled=true); + const fb = document.getElementById('p36-q-fb'); + if(b.dataset.p === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ '+q.why; addXp(3,'p36-q'); bumpProgress('p36',4); } + else { fb.className='feedback fail'; fb.innerHTML='✗ '+q.why; } + document.getElementById('p36-quiz-ok').textContent = ok; + renderMath(w); + }); + }); + renderMath(w); + } + document.getElementById('p36-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; r(); }); + r(); +} +function _p36_dnd(){ + /* по возрастанию n: воздух 1, вода 1.33, стекло 1.5, алмаз 2.42 */ + const items = [ + {id:'a',cat:'r1',html:'воздух (1)'}, + {id:'w',cat:'r2',html:'вода (1{,}33)'}, + {id:'g',cat:'r3',html:'стекло (1{,}5)'}, + {id:'d',cat:'r4',html:'алмаз (2{,}42)'} + ]; + const dnd = setupSorter({ poolId:'p36-dnd-pool', scopeSelector:'#sec-p36', cats:['r1','r2','r3','r4'], items, columnLayout:false }); + document.getElementById('p36-dnd-check').addEventListener('click', ()=>{ + const fb = document.getElementById('p36-dnd-fb'); let wr = 0; + items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wr++; }); + if(wr===0){ fb.className='feedback ok'; fb.innerHTML='✓ +15 XP'; addXp(15,'p36-dnd'); bumpProgress('p36',20); } + else { fb.className='feedback fail'; fb.innerHTML='✗ Ошибок: '+wr+'.'; } + }); + document.getElementById('p36-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); document.getElementById('p36-dnd-fb').style.display='none'; }); +} +function _p36_tasks(){ + const TASKS = [ + {q:'$n_1 = 1$, $n_2 = 1{,}5$, $\\alpha = 30$°. Найди $\\beta$ (°, до 0,1).', ans:19.5, tol:0.5, why:'$\\sin\\beta = \\sin30/1{,}5 = 0{,}333$, $\\beta = 19{,}5$°.'}, + {q:'$n_1 = 1{,}33$ (вода), $n_2 = 1$, $\\alpha = 45$°. Найди $\\beta$ (°).', ans:70.5, tol:1, why:'$\\sin\\beta = 1{,}33 \\sin45/1 = 0{,}941$, $\\beta \\approx 70{,}5$°.'}, + {q:'$\\alpha = 60$°, воздух → стекло (n=1,5). Найди $\\beta$ (°).', ans:35.3, tol:1, why:'$\\sin\\beta = \\sin60/1{,}5 = 0{,}577$, $\\beta \\approx 35{,}3$°.'}, + {q:'В алмазе ($n=2{,}42$) полное внутр. отражение начинается при $\\alpha > $? (°)', ans:24.4, tol:1, why:'$\\sin\\alpha_{кр} = 1/n = 1/2{,}42 = 0{,}413$, $\\alpha_{кр} = 24{,}4$°.'}, + {q:'Свет идёт перпендикулярно поверхности ($\\alpha = 0$). Куда пойдёт?', ans:0, tol:0.5, why:'Без преломления, $\\beta = 0$.'} + ]; + let i = 0, ok = 0, done = 0, aw = false; + function r(){ + const t = TASKS[i]; const w = document.getElementById('p36-task'); + w.innerHTML = '
'+(i+1)+'. '+t.q+'
' + +'
' + +'
'+t.why+'
'; + document.getElementById('p36-task-i').textContent = (i+1); + document.getElementById('p36-task-ok').textContent = ok; + document.getElementById('p36-tgo').addEventListener('click', ()=>{ + const v = parseFloat((document.getElementById('p36-tinp').value || '').replace(',','.')); + const fb = document.getElementById('p36-tfb'); + if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Число.'; return; } + done++; + if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ '+t.why; addXp(4,'p36-t'); bumpProgress('p36',6); } + else { fb.className='feedback fail'; fb.innerHTML='✗ '+t.ans+'. '+t.why; } + document.getElementById('p36-task-ok').textContent = ok; + renderMath(w); + if(done >= TASKS.length && !aw && ok >= 4){ aw = true; setTimeout(()=>{ const f=document.getElementById('p36-tfb'); f.className='feedback ok'; f.innerHTML='✓ +15 XP'; addXp(15,'p36-bonus'); bumpProgress('p36',15); }, 500); } + }); + document.getElementById('p36-thn').addEventListener('click', ()=>{ document.getElementById('p36-tht').classList.toggle('show'); }); + document.getElementById('p36-tn').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; r(); }); + renderMath(w); + } + r(); +} + +/* ======== §37 — Линзы. Оптическая сила ======== */ +function build_p37(){ + const box = document.getElementById('p37-body'); let h = ''; + h += makeCard('theory', 'Что такое линза', '§ 37.1', + '

Линза — прозрачное тело, ограниченное двумя сферическими (или одной плоской и одной сферической) поверхностями.

' + +'

Два типа:

' + +'
    ' + +'
  • Собирающая (выпуклая): толще в середине. Параллельные лучи собираются в точке $F$ — фокусе.
  • ' + +'
  • Рассеивающая (вогнутая): тоньше в середине. Параллельные лучи расходятся, как будто исходят из мнимого фокуса.
  • ' + +'
' + +'

Главная оптическая ось — прямая через центры обеих сфер.

' + ); + h += makeCard('rule', 'Оптическая сила', '§ 37.2', + '

Оптическая сила линзы:

' + +'

$$D = \\dfrac{1}{F}$$

' + +'

Единица — диоптрия (дптр) = $1/$м.

' + +'
    ' + +'
  • $D > 0$ — собирающая линза;
  • ' + +'
  • $D < 0$ — рассеивающая;
  • ' + +'
  • чем больше $|D|$, тем сильнее линза.
  • ' + +'
' + +'

Очки $+2$ дптр $\\to F = 0{,}5$ м (собирающая). Очки $-3$ дптр $\\to F = -0{,}33$ м (рассеивающая).

' + ); + h += makeCard('example', 'Применение', '§ 37.3', + '
    ' + +'
  • Очки.
  • ' + +'
  • Лупа, микроскоп, телескоп.
  • ' + +'
  • Объектив фотоаппарата, проектора.
  • ' + +'
  • Хрусталик глаза.
  • ' + +'
' + ); + h += '
IV-1
Линза и фокусы
' + +'
Выбери тип линзы и фокусное расстояние.
' + +'
' + +'
' + +'
'; + h += '
IV-2
Калькулятор $D = 1/F$
' + +'
' + +'
$D$ = 2.0 дптрТип: собирающая
'; + h += '
IV-3
Собирающая или рассеивающая?
' + +'
' + +'
Собирающая
Рассеивающая
' + +'
' + +'
'; + h += '
IV-4
Тренажёр: 5 задач
' + +'
4+ — +15 XP.
' + +'
' + +'
Задача: 1/5Правильно: 0
'; + box.innerHTML = h + secNavFor('p37') + readButton('p37'); + renderMath(box); wireReadBtn('p37'); + _p37_lens(); _p37_calc(); _p37_dnd(); _p37_tasks(); +} +function _p37_lens(){ + const svg = document.getElementById('p37-sim'); if(!svg) return; + function d(){ + const k = document.getElementById('p37-k').value; + const F = +document.getElementById('p37-f').value; + document.getElementById('p37-fv').textContent = F; + svg.innerHTML = window.OPTICS.thinLens(230, 100, 70, F, k); + } + ['p37-k','p37-f'].forEach(id => document.getElementById(id).addEventListener('input', d)); + document.getElementById('p37-k').addEventListener('change', d); + d(); +} +function _p37_calc(){ + function u(){ + const F = +document.getElementById('p37-fm').value; + document.getElementById('p37-fmv').textContent = F.toFixed(2); + if(F === 0){ + document.getElementById('p37-dv').textContent = '∞'; + document.getElementById('p37-tk').textContent = '—'; + return; + } + const D = 1/F; + document.getElementById('p37-dv').textContent = D.toFixed(2); + document.getElementById('p37-tk').textContent = F > 0 ? 'собирающая' : 'рассеивающая'; + } + document.getElementById('p37-fm').addEventListener('input', u); u(); +} +function _p37_dnd(){ + const items = [ + {id:'a',cat:'c',html:'выпуклая (+)'}, + {id:'b',cat:'c',html:'$D = +3$ дптр'}, + {id:'c',cat:'c',html:'лупа'}, + {id:'d',cat:'c',html:'хрусталик глаза'}, + {id:'e',cat:'d',html:'вогнутая (−)'}, + {id:'f',cat:'d',html:'$D = -2$ дптр'}, + {id:'g',cat:'d',html:'очки для близоруких'}, + {id:'h',cat:'d',html:'рассеивает лучи'} + ]; + const dnd = setupSorter({ poolId:'p37-dnd-pool', scopeSelector:'#sec-p37', cats:['c','d'], items, columnLayout:false }); + document.getElementById('p37-dnd-check').addEventListener('click', ()=>{ + const fb = document.getElementById('p37-dnd-fb'); let wr = 0; + items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wr++; }); + if(wr===0){ fb.className='feedback ok'; fb.innerHTML='✓ +15 XP'; addXp(15,'p37-dnd'); bumpProgress('p37',20); renderMath(fb); } + else { fb.className='feedback fail'; fb.innerHTML='✗ Ошибок: '+wr+'.'; renderMath(fb); } + }); + document.getElementById('p37-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); document.getElementById('p37-dnd-fb').style.display='none'; }); +} +function _p37_tasks(){ + const TASKS = [ + {q:'$F = 25$ см $= 0{,}25$ м. Найди $D$ (дптр).', ans:4, tol:0.1, why:'$D = 1/0{,}25 = 4$ дптр.'}, + {q:'$D = +5$ дптр. Найди $F$ (см).', ans:20, tol:0.5, why:'$F = 1/5 = 0{,}2$ м $= 20$ см.'}, + {q:'$D = -2$ дптр. Какой $F$ (м, с знаком)?', ans:-0.5, tol:0.05, why:'$F = 1/(-2) = -0{,}5$ м.'}, + {q:'Очки +1.5 дптр. Какое фокусное расстояние (см)?', ans:67, tol:2, why:'$F = 1/1{,}5 \\approx 0{,}67$ м $= 67$ см.'}, + {q:'У хрусталика глаза $D = 60$ дптр. Найди $F$ в мм.', ans:17, tol:1, why:'$F = 1/60 \\approx 0{,}017$ м = $17$ мм.'} + ]; + let i = 0, ok = 0, done = 0, aw = false; + function r(){ + const t = TASKS[i]; const w = document.getElementById('p37-task'); + w.innerHTML = '
'+(i+1)+'. '+t.q+'
' + +'
' + +'
'+t.why+'
'; + document.getElementById('p37-task-i').textContent = (i+1); + document.getElementById('p37-task-ok').textContent = ok; + document.getElementById('p37-tgo').addEventListener('click', ()=>{ + const v = parseFloat((document.getElementById('p37-tinp').value || '').replace(',','.')); + const fb = document.getElementById('p37-tfb'); + if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Число.'; return; } + done++; + if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ '+t.why; addXp(4,'p37-t'); bumpProgress('p37',6); } + else { fb.className='feedback fail'; fb.innerHTML='✗ '+t.ans+'. '+t.why; } + document.getElementById('p37-task-ok').textContent = ok; + renderMath(w); + if(done >= TASKS.length && !aw && ok >= 4){ aw = true; setTimeout(()=>{ const f=document.getElementById('p37-tfb'); f.className='feedback ok'; f.innerHTML='✓ +15 XP'; addXp(15,'p37-bonus'); bumpProgress('p37',15); }, 500); } + }); + document.getElementById('p37-thn').addEventListener('click', ()=>{ document.getElementById('p37-tht').classList.toggle('show'); }); + document.getElementById('p37-tn').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; r(); }); + renderMath(w); + } + r(); +} + +/* ======== §38 — Построение изображений в линзах ======== */ +function build_p38(){ + const box = document.getElementById('p38-body'); let h = ''; + h += makeCard('theory', 'Формула тонкой линзы', '§ 38.1', + '

Расстояния от предмета $d$ и от изображения $f$ до линзы связаны:

' + +'

$$\\dfrac{1}{F} = \\dfrac{1}{d} + \\dfrac{1}{f}$$

' + +'

Из неё $f = \\dfrac{dF}{d - F}$.

' + +'

Если $f > 0$ — изображение действительное (с той стороны линзы, на экране). Если $f < 0$ — мнимое (с той же стороны, где предмет).

' + ); + h += makeCard('rule', '3 «золотых» луча', '§ 38.2', + '
    ' + +'
  1. Луч через центр линзы — не преломляется.
  2. ' + +'
  3. Луч параллельный оси — после линзы проходит через дальний $F$.
  4. ' + +'
  5. Луч через ближний $F$ — после линзы идёт параллельно оси.
  6. ' + +'
' + +'

Любые два из этих лучей пересекутся в точке изображения.

' + ); + h += makeCard('example', 'Зависимость от расстояния', '§ 38.3', + '

В собирающей линзе ($F > 0$):

' + +'
    ' + +'
  • $d > 2F$ — изображение уменьшенное, перевёрнутое, действительное (фотоаппарат).
  • ' + +'
  • $d = 2F$ — равное, перевёрнутое, действительное.
  • ' + +'
  • $F < d < 2F$ — увеличенное, перевёрнутое, действительное (проектор).
  • ' + +'
  • $d = F$ — на бесконечности (параллельные лучи).
  • ' + +'
  • $d < F$ — увеличенное, прямое, мнимое (лупа).
  • ' + +'
' + ); + h += '
IV-1
Конструктор изображения
' + +'
Меняй $F$ и $d$ — наблюдай, как меняется изображение. Зелёный — действительное, фиолетовый пунктир — мнимое.
' + +'
' + +'
' + +'' + +'
' + +'$f$ = 30 см' + +'Увеличение: 2.0x' + +'Тип: действительное, перевёрнутое
'; + h += '
IV-2
Какое изображение?
' + +'
Тип изображения по условиям.
' + +'
' + +'
' + +'
Раунд: 1/5Правильно: 0
'; + h += '
IV-3
DnD устройств
' + +'
К какому случаю относится прибор?
' + +'
' + +'
$d > 2F$ (фото)
$F < d < 2F$ (проектор)
$d < F$ (лупа)
' + +'
' + +'
'; + h += '
IV-4
Тренажёр: 5 задач
' + +'
4+ — +15 XP.
' + +'
' + +'
Задача: 1/5Правильно: 0
'; + box.innerHTML = h + secNavFor('p38') + readButton('p38'); + renderMath(box); wireReadBtn('p38'); + _p38_constr(); _p38_quiz(); _p38_dnd(); _p38_tasks(); +} +function _p38_constr(){ + const svg = document.getElementById('p38-sim'); if(!svg) return; + function d(){ + const F = +document.getElementById('p38-f').value; + const dist = +document.getElementById('p38-d').value; + document.getElementById('p38-fv').textContent = F; + document.getElementById('p38-dv').textContent = dist; + const res = window.OPTICS.buildLensImage(F, dist, 30); + const f = res.f, h2 = res.h2, virtual = res.virtual; + document.getElementById('p38-fs').textContent = isFinite(f) ? f.toFixed(1) : '∞'; + document.getElementById('p38-mg').textContent = isFinite(f) ? Math.abs(h2/30).toFixed(2) : '∞'; + const type = virtual ? (h2 > 0 ? 'мнимое, прямое, увеличенное' : 'мнимое, прямое') : (h2 < 0 ? 'действительное, перевёрнутое' : 'действительное'); + document.getElementById('p38-tp').textContent = type; + + /* Рисуем SVG */ + const cx = 300, cy = 130; + const sc = 4; /* px per см */ + let s = ''; + /* ось */ + s += ''; + /* линза */ + s += window.OPTICS.thinLens(cx, cy, 60, F*sc, 'converging'); + /* предмет — слева */ + const objX = cx - dist*sc; + const objH = 60; /* высота 30 см = 60 px */ + s += ''; + s += ''; + /* изображение */ + if(isFinite(f) && !virtual){ + const imgX = cx + f*sc; + const imgY = cy - h2*sc/15; /* h2 от исходн. h=30 — масштабируем */ + s += ''; + const tip = cy - h2*2; + s += ''; + } else if(isFinite(f) && virtual){ + const imgX = cx + f*sc; /* отрицательное f → слева */ + s += ''; + const tip = cy - h2*2; + s += ''; + } + svg.innerHTML = s; + } + document.getElementById('p38-f').addEventListener('input', d); + document.getElementById('p38-d').addEventListener('input', d); + d(); +} +function _p38_quiz(){ + const QS = [ + {sit:'$d > 2F$ (например, $d = 30$, $F = 10$)', ans:'A', why:'Уменьшенное, перевёрнутое, действительное.'}, + {sit:'$d = 2F$', ans:'B', why:'Равное, перевёрнутое, действительное.'}, + {sit:'$F < d < 2F$ (проектор)', ans:'C', why:'Увеличенное, перевёрнутое, действительное.'}, + {sit:'$d < F$ (лупа)', ans:'D', why:'Увеличенное, прямое, мнимое.'}, + {sit:'$d = F$', ans:'E', why:'На бесконечности — параллельные лучи.'} + ]; + let i = 0, ok = 0; + function r(){ + const q = QS[i]; const w = document.getElementById('p38-quiz'); + w.innerHTML = '
'+q.sit+'
' + +'
' + +'' + +'' + +'' + +'' + +'' + +'
'; + document.getElementById('p38-quiz-r').textContent = (i+1); + document.getElementById('p38-quiz-ok').textContent = ok; + w.querySelectorAll('[data-p]').forEach(b=>{ + b.addEventListener('click', ()=>{ + if(b.disabled) return; w.querySelectorAll('[data-p]').forEach(x=>x.disabled=true); + const fb = document.getElementById('p38-q-fb'); + if(b.dataset.p === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ '+q.why; addXp(3,'p38-q'); bumpProgress('p38',4); } + else { fb.className='feedback fail'; fb.innerHTML='✗ '+q.why; } + document.getElementById('p38-quiz-ok').textContent = ok; + }); + }); + } + document.getElementById('p38-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; r(); }); + r(); +} +function _p38_dnd(){ + const items = [ + {id:'p',cat:'far',html:'фотоаппарат'}, + {id:'eye',cat:'far',html:'глаз (далёкий предмет)'}, + {id:'mp',cat:'mid',html:'проектор'}, + {id:'film',cat:'mid',html:'кинопроектор'}, + {id:'l1',cat:'lupa',html:'лупа'}, + {id:'l2',cat:'lupa',html:'часы под лупой'} + ]; + const dnd = setupSorter({ poolId:'p38-dnd-pool', scopeSelector:'#sec-p38', cats:['far','mid','lupa'], items, columnLayout:false }); + document.getElementById('p38-dnd-check').addEventListener('click', ()=>{ + const fb = document.getElementById('p38-dnd-fb'); let wr = 0; + items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wr++; }); + if(wr===0){ fb.className='feedback ok'; fb.innerHTML='✓ +15 XP'; addXp(15,'p38-dnd'); bumpProgress('p38',20); } + else { fb.className='feedback fail'; fb.innerHTML='✗ Ошибок: '+wr+'.'; } + }); + document.getElementById('p38-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); document.getElementById('p38-dnd-fb').style.display='none'; }); +} +function _p38_tasks(){ + const TASKS = [ + {q:'$F = 20$ см, $d = 30$ см. Найди $f$ (см).', ans:60, tol:1, why:'$f = dF/(d-F) = 30\\cdot20/(30-20) = 60$ см.'}, + {q:'$F = 10$ см, $d = 15$ см. Найди $f$ (см).', ans:30, tol:1, why:'$f = 15\\cdot10/5 = 30$ см.'}, + {q:'$F = 20$ см, $d = 60$ см. Найди $f$ (см).', ans:30, tol:1, why:'$f = 60\\cdot20/40 = 30$ см.'}, + {q:'Предмет высотой 5 см, $d = 30$ см, $f = 60$ см. Найди высоту изображения (см).', ans:10, tol:0.5, why:'$h_2 = h \\cdot f/d = 5 \\cdot 60/30 = 10$ см.'}, + {q:'$F = 15$ см, $d = 10$ см (лупа). Найди $|f|$ (см).', ans:30, tol:1, why:'$f = 10\\cdot15/(10-15) = -30$, |f| = 30 см. Мнимое.'} + ]; + let i = 0, ok = 0, done = 0, aw = false; + function r(){ + const t = TASKS[i]; const w = document.getElementById('p38-task'); + w.innerHTML = '
'+(i+1)+'. '+t.q+'
' + +'
' + +'
'+t.why+'
'; + document.getElementById('p38-task-i').textContent = (i+1); + document.getElementById('p38-task-ok').textContent = ok; + document.getElementById('p38-tgo').addEventListener('click', ()=>{ + const v = parseFloat((document.getElementById('p38-tinp').value || '').replace(',','.')); + const fb = document.getElementById('p38-tfb'); + if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Число.'; return; } + done++; + if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ '+t.why; addXp(4,'p38-t'); bumpProgress('p38',6); } + else { fb.className='feedback fail'; fb.innerHTML='✗ '+t.ans+'. '+t.why; } + document.getElementById('p38-task-ok').textContent = ok; + renderMath(w); + if(done >= TASKS.length && !aw && ok >= 4){ aw = true; setTimeout(()=>{ const f=document.getElementById('p38-tfb'); f.className='feedback ok'; f.innerHTML='✓ +15 XP'; addXp(15,'p38-bonus'); bumpProgress('p38',15); }, 500); } + }); + document.getElementById('p38-thn').addEventListener('click', ()=>{ document.getElementById('p38-tht').classList.toggle('show'); }); + document.getElementById('p38-tn').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; r(); }); + renderMath(w); + } + r(); +} + +/* ======== §39 — Глаз как оптическая система ======== */ +function build_p39(){ + const box = document.getElementById('p39-body'); let h = ''; + h += makeCard('theory', 'Устройство глаза', '§ 39.1', + '

Глаз — сложная оптическая система:

' + +'
    ' + +'
  • Роговица — прозрачная передняя оболочка, преломляет свет.
  • ' + +'
  • Зрачок — отверстие в радужной оболочке, регулирует количество света.
  • ' + +'
  • Хрусталик — биологическая собирающая линза с переменным $F$.
  • ' + +'
  • Сетчатка — задняя стенка глаза, на ней формируется изображение.
  • ' + +'
  • Зрительный нерв — передаёт сигнал в мозг.
  • ' + +'
' + ); + h += makeCard('rule', 'Аккомодация', '§ 39.2', + '

Расстояние от хрусталика до сетчатки фиксировано. А расстояние до предметов меняется. Как глаз приспосабливается?

' + +'

Мышцы вокруг хрусталика меняют его кривизну (форму):

' + +'
    ' + +'
  • Далёкий предмет: хрусталик уплощается, $F$ растёт.
  • ' + +'
  • Близкий предмет: хрусталик округляется, $F$ уменьшается.
  • ' + +'
' + +'

Это и есть аккомодация.

' + ); + h += makeCard('example', 'Расстояние наилучшего зрения', '§ 39.3', + '

Расстояние, на котором глаз видит чётко без напряжения, — около 25 см. На таком расстоянии обычно держат книгу при чтении.

' + +'

Ближе глаз перенапрягается, дальше — теряется чёткость деталей.

' + ); + h += '
IV-1
Аккомодация хрусталика
' + +'
Меняй расстояние до предмета — увидь, как меняется форма хрусталика.
' + +'
' + +'
'; + h += '
IV-2
Элементы глаза
' + +'
Какая часть глаза за что отвечает?
' + +'
' + +'
' + +'
Раунд: 1/5Правильно: 0
'; + h += '
IV-3
Сортировка частей глаза
' + +'
' + +'
Оптические
Регистрация / нерв
' + +'
' + +'
'; + h += '
IV-4
Тренажёр: 6 MCQ
' + +'
' + +'
Вопрос: 1/6Правильно: 0
'; + box.innerHTML = h + secNavFor('p39') + readButton('p39'); + renderMath(box); wireReadBtn('p39'); + _p39_eye(); _p39_quiz(); _p39_dnd(); _p39_mcq(); +} +function _p39_eye(){ + const svg = document.getElementById('p39-sim'); if(!svg) return; + function d(){ + const dval = +document.getElementById('p39-d').value; + /* 0 — далёкий, 100 — близкий */ + document.getElementById('p39-dv').textContent = dval < 30 ? 'далёкий (бесконечность)' : dval < 70 ? 'на расстоянии 1 м' : 'близкий (25 см)'; + const acc = dval / 100; + let s = ''; + /* «предмет» слева */ + s += window.OPTICS.lightObject(60, 130, 40, 'arrow'); + s += 'предмет'; + /* линии лучей */ + s += ''; + s += ''; + /* глаз справа */ + s += window.OPTICS.eyeDiagram(330, 115, 70, acc); + /* подпись аккомодации */ + s += 'хрусталик '+(acc < 0.3 ? 'уплощён' : acc < 0.7 ? 'средний' : 'округлён')+''; + svg.innerHTML = s; + } + document.getElementById('p39-d').addEventListener('input', d); d(); +} +function _p39_quiz(){ + const QS = [ + {sit:'Эта часть преломляет свет на входе.', opts:['Сетчатка','Роговица','Зрачок','Нерв'], ans:1, why:'Роговица — главный преломляющий элемент.'}, + {sit:'Она меняет $F$ при изменении расстояния.', opts:['Сетчатка','Хрусталик','Радужка','Зрачок'], ans:1, why:'Хрусталик меняет форму — аккомодация.'}, + {sit:'На ней формируется изображение.', opts:['Радужка','Сетчатка','Зрачок','Зр. нерв'], ans:1, why:'Сетчатка — «экран» глаза.'}, + {sit:'Она регулирует количество света.', opts:['Радужка/зрачок','Хрусталик','Роговица','Сетчатка'], ans:0, why:'На ярком свету зрачок сужается.'}, + {sit:'Это сетчатка передаёт сигнал в мозг через…', opts:['артерию','зрительный нерв','лимфу','аккомодацию'], ans:1, why:'Зрительный нерв.'} + ]; + let i = 0, ok = 0; + function r(){ + const q = QS[i]; const w = document.getElementById('p39-quiz'); + let html = '
'+(i+1)+'. '+q.sit+'
'; + q.opts.forEach((o,k)=>{ html += ''; }); + html += '
'; + w.innerHTML = html; + document.getElementById('p39-quiz-r').textContent = (i+1); + document.getElementById('p39-quiz-ok').textContent = ok; + w.querySelectorAll('[data-k]').forEach(b=>{ + b.addEventListener('click', ()=>{ + if(b.disabled) return; w.querySelectorAll('[data-k]').forEach(x=>x.disabled=true); + const k = +b.dataset.k; const fb = document.getElementById('p39-q-fb'); + if(k===q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ '+q.why; addXp(3,'p39-q'); bumpProgress('p39',4); } + else { fb.className='feedback fail'; fb.innerHTML='✗ '+q.why; } + document.getElementById('p39-quiz-ok').textContent = ok; + }); + }); + } + document.getElementById('p39-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; r(); }); + r(); +} +function _p39_dnd(){ + const items = [ + {id:'r',cat:'op',html:'роговица'},{id:'h',cat:'op',html:'хрусталик'},{id:'z',cat:'op',html:'зрачок'},{id:'i',cat:'op',html:'радужка'}, + {id:'s',cat:'nr',html:'сетчатка'},{id:'n',cat:'nr',html:'зрительный нерв'},{id:'c',cat:'nr',html:'колбочки и палочки'},{id:'br',cat:'nr',html:'мозг (анализ)'} + ]; + const dnd = setupSorter({ poolId:'p39-dnd-pool', scopeSelector:'#sec-p39', cats:['op','nr'], items, columnLayout:false }); + document.getElementById('p39-dnd-check').addEventListener('click', ()=>{ + const fb = document.getElementById('p39-dnd-fb'); let wr = 0; + items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wr++; }); + if(wr===0){ fb.className='feedback ok'; fb.innerHTML='✓ +15 XP'; addXp(15,'p39-dnd'); bumpProgress('p39',20); } + else { fb.className='feedback fail'; fb.innerHTML='✗ Ошибок: '+wr+'.'; } + }); + document.getElementById('p39-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); document.getElementById('p39-dnd-fb').style.display='none'; }); +} +function _p39_mcq(){ + const QS = [ + {q:'Что играет роль линзы в глазу?', opts:['роговица','хрусталик','сетчатка','зрачок'], ans:1, why:'Хрусталик с переменным F.'}, + {q:'Что изменяется при аккомодации?', opts:['расст. до сетчатки','форма хрусталика','цвет радужки','диаметр зрачка'], ans:1, why:'Мышцы меняют форму хрусталика.'}, + {q:'Где формируется изображение?', opts:['в зрачке','на роговице','на сетчатке','в нерве'], ans:2, why:'На сетчатке.'}, + {q:'Расстояние наилучшего зрения для здорового глаза?', opts:['10 см','25 см','1 м','5 м'], ans:1, why:'25 см.'}, + {q:'Каким будет изображение на сетчатке?', opts:['прямое, мнимое','перевёрн., действ.','прямое, действ.','мнимое'], ans:1, why:'Глаз — собирающая система: перевёрнутое действительное (мозг переворачивает).'}, + {q:'Что НЕ оптический элемент глаза?', opts:['роговица','хрусталик','сетчатка','нерв'], ans:3, why:'Нерв передаёт сигнал, не преломляет свет.'} + ]; + let i = 0, ok = 0, done = 0, aw = false; + function r(){ + const q = QS[i]; const w = document.getElementById('p39-mcq'); + let h = '
'+(i+1)+'. '+q.q+'
'; + q.opts.forEach((o,k)=>{ h += ''; }); + h += '
'; + w.innerHTML = h; + document.getElementById('p39-mcq-i').textContent = (i+1); + document.getElementById('p39-mcq-ok').textContent = ok; + w.querySelectorAll('[data-k]').forEach(b=>{ + b.addEventListener('click', ()=>{ + if(b.disabled) return; w.querySelectorAll('[data-k]').forEach(x=>x.disabled=true); + const k = +b.dataset.k; const fb = document.getElementById('p39-mcq-fb'); + if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='✓ '+q.why; addXp(2,'p39-mcq'); bumpProgress('p39',3); } + else { done++; fb.className='feedback fail'; fb.innerHTML='✗ '+q.why; } + document.getElementById('p39-mcq-ok').textContent = ok; + if(done >= QS.length && !aw && ok >= 4){ aw = true; setTimeout(()=>{ const f=document.getElementById('p39-mcq-fb'); f.className='feedback ok'; f.innerHTML='✓ +15 XP'; addXp(15,'p39-bonus'); bumpProgress('p39',15); }, 500); } + }); + }); + document.getElementById('p39-mcq-n').addEventListener('click', ()=>{ i=(i+1)%QS.length; r(); }); + } + r(); +} + +/* ======== §40 — Дефекты зрения. Очки ======== */ +function build_p40(){ + const box = document.getElementById('p40-body'); let h = ''; + h += makeCard('theory', 'Близорукость', '§ 40.1', + '

Близорукость (миопия) — изображение далёких предметов фокусируется перед сетчаткой.

' + +'

Причина: глаз слегка вытянут, или хрусталик слишком сильный. Человек хорошо видит вблизи, плохо — вдаль.

' + +'

Лечение: рассеивающие линзы ($D < 0$). Они «отодвигают» фокус на сетчатку.

' + ); + h += makeCard('rule', 'Дальнозоркость', '§ 40.2', + '

Дальнозоркость (гиперметропия) — изображение фокусируется за сетчаткой.

' + +'

Причина: глаз короче нормы, или хрусталик слабоват. Человек плохо видит вблизи.

' + +'

Лечение: собирающие линзы ($D > 0$). Они «приближают» фокус к сетчатке.

' + ); + h += makeCard('example', 'Как подобрать очки', '§ 40.3', + '

Сила линзы $D$ подбирается врачом-офтальмологом по таблицам.

' + +'
    ' + +'
  • $-1$ … $-3$ дптр — лёгкая близорукость.
  • ' + +'
  • $-3$ … $-6$ дптр — средняя.
  • ' + +'
  • $-6$ дптр и больше — сильная.
  • ' + +'
' + +'

Современные альтернативы: контактные линзы, лазерная коррекция.

' + ); + h += '
IV-1
Очки исправляют зрение
' + +'
Выбери дефект — увидь, какие очки нужны.
' + +'
' + +'' + +'
Нужны очки: собирающие, $D > 0$
'; + h += '
IV-2
Какие очки?
' + +'
Подбери тип линз для дефекта.
' + +'
' + +'
' + +'
Раунд: 1/5Правильно: 0
'; + h += '
IV-3
DnD признаков
' + +'
' + +'
Близорукость
Дальнозоркость
' + +'
' + +'
'; + h += '
IV-4
Тренажёр: 5 задач
' + +'
4+ — +15 XP.
' + +'
' + +'
Задача: 1/5Правильно: 0
'; + box.innerHTML = h + secNavFor('p40') + readButton('p40'); + renderMath(box); wireReadBtn('p40'); + _p40_glass(); _p40_quiz(); _p40_dnd(); _p40_tasks(); +} +function _p40_glass(){ + const svg = document.getElementById('p40-sim'); if(!svg) return; + function d(){ + const def = document.getElementById('p40-def').value; + let s = ''; + const cx = 320, cy = 100; + /* глаз */ + s += window.OPTICS.eyeDiagram(cx, cy, 50, 0.3); + /* лучи входящие */ + const labelLens = def === 'm' ? 'рассеивающие, $D < 0$' : def === 'h' ? 'собирающие, $D > 0$' : 'не нужны'; + document.getElementById('p40-lens').innerHTML = labelLens; + /* для близорукости: фокус ПЕРЕД сетчаткой; рассеивающая линза «отодвигает» */ + /* для дальнозоркости: фокус ЗА; собирающая «приближает» */ + /* нарисуем фокусное пятно условно */ + let focusX = 0; + let lensType = ''; + if(def === 'm'){ focusX = cx - 5; lensType = 'diverging'; } + else if(def === 'h'){ focusX = cx + 60; lensType = 'converging'; } + else { focusX = cx + 40; } + /* очки спереди глаза */ + if(def !== 'n'){ + s += window.OPTICS.thinLens(200, cy, 50, 80, lensType); + } + /* лучи */ + s += ''; + s += ''; + s += ''; + s += ''; + s += ''; + s += 'фокус'; + if(def === 'n') s += 'нормальное зрение'; + if(def === 'm') s += 'близорукость: фокус до сетчатки'; + if(def === 'h') s += 'дальнозоркость: фокус за сетчаткой'; + svg.innerHTML = s; + } + document.getElementById('p40-def').addEventListener('change', d); d(); +} +function _p40_quiz(){ + const QS = [ + {sit:'У человека близорукость', ans:'-', why:'Нужны рассеивающие, $D < 0$.'}, + {sit:'Дальнозоркость', ans:'+', why:'Нужны собирающие, $D > 0$.'}, + {sit:'У школьника плохо видит вдаль', ans:'-', why:'Признак близорукости.'}, + {sit:'У бабушки плохо видит вблизи (мелкий шрифт)', ans:'+', why:'Возрастная дальнозоркость.'}, + {sit:'$D = +2$ дптр прописал врач', ans:'+', why:'Собирающие — для дальнозоркости.'} + ]; + let i = 0, ok = 0; + function r(){ + const q = QS[i]; const w = document.getElementById('p40-quiz'); + w.innerHTML = '
'+q.sit+'
' + +'
' + +''; + document.getElementById('p40-quiz-r').textContent = (i+1); + document.getElementById('p40-quiz-ok').textContent = ok; + w.querySelectorAll('[data-p]').forEach(b=>{ + b.addEventListener('click', ()=>{ + if(b.disabled) return; w.querySelectorAll('[data-p]').forEach(x=>x.disabled=true); + const fb = document.getElementById('p40-q-fb'); + if(b.dataset.p === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ '+q.why; addXp(3,'p40-q'); bumpProgress('p40',4); } + else { fb.className='feedback fail'; fb.innerHTML='✗ '+q.why; } + document.getElementById('p40-quiz-ok').textContent = ok; + renderMath(w); + }); + }); + renderMath(w); + } + document.getElementById('p40-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; r(); }); + r(); +} +function _p40_dnd(){ + const items = [ + {id:'a',cat:'m',html:'фокус ПЕРЕД сетчаткой'}, + {id:'b',cat:'m',html:'плохо видит вдаль'}, + {id:'c',cat:'m',html:'нужны рассеив. линзы'}, + {id:'d',cat:'m',html:'$D < 0$'}, + {id:'e',cat:'h',html:'фокус ЗА сетчаткой'}, + {id:'f',cat:'h',html:'плохо видит вблизи'}, + {id:'g',cat:'h',html:'нужны собир. линзы'}, + {id:'i',cat:'h',html:'$D > 0$'} + ]; + const dnd = setupSorter({ poolId:'p40-dnd-pool', scopeSelector:'#sec-p40', cats:['m','h'], items, columnLayout:false }); + document.getElementById('p40-dnd-check').addEventListener('click', ()=>{ + const fb = document.getElementById('p40-dnd-fb'); let wr = 0; + items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wr++; }); + if(wr===0){ fb.className='feedback ok'; fb.innerHTML='✓ +15 XP'; addXp(15,'p40-dnd'); bumpProgress('p40',20); renderMath(fb); } + else { fb.className='feedback fail'; fb.innerHTML='✗ Ошибок: '+wr+'.'; renderMath(fb); } + }); + document.getElementById('p40-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); document.getElementById('p40-dnd-fb').style.display='none'; }); +} +function _p40_tasks(){ + const TASKS = [ + {q:'$F = -0{,}5$ м. Найди $D$ (дптр).', ans:-2, tol:0.1, why:'$D = 1/-0{,}5 = -2$ дптр.'}, + {q:'Очки $D = +1{,}5$ дптр. Какой $F$ (см)?', ans:67, tol:2, why:'$F = 1/1{,}5 \\approx 0{,}67$ м = 67 см.'}, + {q:'Близорукому нужны $D = -3$ дптр. Какой |F| (см)?', ans:33, tol:1, why:'|F| = 1/3 м = 33 см.'}, + {q:'Какой знак $D$ для очков от близорукости? (введи +1 или -1)', ans:-1, tol:0.1, why:'Рассеивающие, $D < 0$.'}, + {q:'У бабушки прописали +2 дптр. Какой её дефект? (введи 1=близорукость, 2=дальнозоркость)', ans:2, tol:0.1, why:'$D > 0$ → собирающие → дальнозоркость.'} + ]; + let i = 0, ok = 0, done = 0, aw = false; + function r(){ + const t = TASKS[i]; const w = document.getElementById('p40-task'); + w.innerHTML = '
'+(i+1)+'. '+t.q+'
' + +'
' + +'
'+t.why+'
'; + document.getElementById('p40-task-i').textContent = (i+1); + document.getElementById('p40-task-ok').textContent = ok; + document.getElementById('p40-tgo').addEventListener('click', ()=>{ + const v = parseFloat((document.getElementById('p40-tinp').value || '').replace(',','.')); + const fb = document.getElementById('p40-tfb'); + if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Число.'; return; } + done++; + if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ '+t.why; addXp(4,'p40-t'); bumpProgress('p40',6); } + else { fb.className='feedback fail'; fb.innerHTML='✗ '+t.ans+'. '+t.why; } + document.getElementById('p40-task-ok').textContent = ok; + renderMath(w); + if(done >= TASKS.length && !aw && ok >= 4){ aw = true; setTimeout(()=>{ const f=document.getElementById('p40-tfb'); f.className='feedback ok'; f.innerHTML='✓ +15 XP'; addXp(15,'p40-bonus'); bumpProgress('p40',15); }, 500); } + }); + document.getElementById('p40-thn').addEventListener('click', ()=>{ document.getElementById('p40-tht').classList.toggle('show'); }); + document.getElementById('p40-tn').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; r(); }); + renderMath(w); + } + r(); +} + +/* ======== ФИНАЛ ГЛАВЫ 3 ======== */ +function build_final3(){ + const box = document.getElementById('final3-body'); let h = ''; + h += '
' + +'
'+ICONS.rule+'
Шпаргалка главы 3
' + +'
' + +'
$c$: $3 \\cdot 10^8$ м/с
' + +'
Тень: точечный источник $\\to$ только тень
' + +'
Отражение: $\\alpha = \\beta$
' + +'
Плоское зеркало: мнимое, прямое, равное
' + +'
Снеллиус: $\\sin\\alpha/\\sin\\beta = n_2/n_1$
' + +'
$n$ воды: 1,33; стекла: 1,5
' + +'
$D$: $D = 1/F$, дптр
' + +'
Линза: $1/F = 1/d + 1/f$
' + +'
Глаз: хрусталик меняет $F$ (аккомодация)
' + +'
Близорукость: $D < 0$; дальнозоркость: $D > 0$
' + +'
'; + + const BOSSES = [ + {n:1, title:'Скорость света', q:'За какое время свет пройдёт 600 000 км (мин)?', hint:'$t = 6\\cdot10^8 / 3\\cdot10^8 = 2$ с. В минутах: $1/30 \\approx 0{,}033$ мин.', ans:0.033, tol:0.005, step:'0.001'}, + {n:2, title:'Закон отражения', q:'$\\alpha = 25$°. Найди $\\beta$ (°).', hint:'$\\beta = \\alpha = 25$°.', ans:25, tol:0.5, step:'0.5'}, + {n:3, title:'Преломление', q:'$\\alpha = 30$°, воздух → стекло (n=1{,}5). Найди $\\beta$ (°, до 0,1).', hint:'$\\sin\\beta = \\sin30/1{,}5 = 0{,}333$, $\\beta \\approx 19{,}5$°.', ans:19.5, tol:0.5, step:'0.1'}, + {n:4, title:'$D$ линзы', q:'$F = 0{,}25$ м. Найди $D$ (дптр).', hint:'$D = 1/0{,}25 = 4$ дптр.', ans:4, tol:0.1, step:'0.1'}, + {n:5, title:'Тонкая линза', q:'$F = 20$ см, $d = 30$ см. Найди $f$ (см).', hint:'$f = 30\\cdot20/(30-20) = 60$ см.', ans:60, tol:1, step:'1'}, + {n:6, title:'Очки', q:'Близорукому прописали $|F| = 50$ см. Какое $D$ (дптр)?', hint:'$D = 1/F = 1/(-0{,}5) = -2$ дптр.', ans:-2, tol:0.1, step:'0.1'}, + {n:7, title:'Магистр света', q:'Угол падения на стекло $\\alpha = 60$°, $n = 1{,}5$. Угол между падающим и преломлённым (°, до 0,1).', hint:'$\\sin\\beta = \\sin60/1{,}5 \\approx 0{,}577$, $\\beta \\approx 35{,}3$°. Угол между падающим (продолж.) и преломлённым: $\\alpha - \\beta \\approx 24{,}7$° в относ. направ-х. Но угол между фактич. (падающий до границы) и преломлённым (после) — $180 - \\alpha - \\beta = 180 - 95{,}3 \\approx 84{,}7$°.', ans:84.7, tol:1, step:'0.1'} + ]; + + h += '
'+ICONS.example+'
7 боссов главы 3
' + +'
' + +'Боссов побеждено: 0 / 7' + +'
' + +'
' + +'
' + +'
' + +'
'; + + box.innerHTML = h + secNavFor('final3') + readButton('final3'); + renderMath(box); wireReadBtn('final3'); + _initFinal3_bosses(BOSSES); +} +function _initFinal3_bosses(BOSSES){ + const KEY = 'physics8_ch3_bosses'; + function loadState(){ try { return JSON.parse(localStorage.getItem(KEY) || '{}') || {}; } catch(e){ return {}; } } + function saveState(s){ try { localStorage.setItem(KEY, JSON.stringify(s)); } catch(e){} } + function updateBar(){ + const s = loadState(); + let won = 0; for(const k in s) if(s[k]) won++; + document.getElementById('f3-won').textContent = won; + document.getElementById('f3-bar').style.width = Math.round(won*100/BOSSES.length)+'%'; + if(won >= BOSSES.length && !STATE.achievements.has('light_master')){ + addXp(50, 'light-master'); + achievement('light_master'); + } + return won; + } + function renderAll(){ + const cont = document.getElementById('f3-bosses'); + const state = loadState(); + let html = ''; + BOSSES.forEach(b=>{ + const solved = state[b.n]; + html += '
' + +'
Босс '+b.n+''+b.title+'
' + +'
'+b.q+'
' + +'
' + +'' + +'' + +'' + +'
' + +'' + +'
'+(solved?'✓ Победа! +10 XP. Босс повержен.':'')+'
' + +'
'; + }); + cont.innerHTML = html; + BOSSES.forEach(b=>{ + const go = document.getElementById('f3-b'+b.n+'-go'); + const inp = document.getElementById('f3-b'+b.n+'-inp'); + const fb = document.getElementById('f3-b'+b.n+'-fb'); + const ht = document.getElementById('f3-b'+b.n+'-ht'); + const hintBtn = document.getElementById('f3-b'+b.n+'-hint'); + if(hintBtn) hintBtn.addEventListener('click', ()=>{ ht.style.display = ht.style.display==='block'?'none':'block'; }); + if(!go || go.disabled) return; + go.addEventListener('click', ()=>{ + const v = parseFloat((inp.value || '').replace(',','.')); + if(isNaN(v)){ fb.style.display='block'; fb.className='feedback fail'; fb.innerHTML='Введите число.'; return; } + if(Math.abs(v - b.ans) < b.tol){ + fb.style.display='block'; fb.className='feedback ok'; fb.innerHTML='✓ Победа! +10 XP. '+b.hint; + go.disabled = true; inp.disabled = true; + document.getElementById('f3-boss-'+b.n).classList.add('solved'); + const s = loadState(); + if(!s[b.n]){ s[b.n]=true; saveState(s); addXp(10,'f3-boss-'+b.n); bumpProgress('final3', 14); } + updateBar(); + renderMath(fb); + } else { + fb.style.display='block'; fb.className='feedback fail'; fb.innerHTML='✗ Не то. Перепроверь и попробуй снова.'; + } + }); + inp.addEventListener('keydown', e=>{ if(e.key === 'Enter') go.click(); }); + }); + renderMath(cont); + updateBar(); + } + renderAll(); } function init(){