diff --git a/frontend/textbooks/geometry_11_ch4.html b/frontend/textbooks/geometry_11_ch4.html index 978a60a..f25182b 100644 --- a/frontend/textbooks/geometry_11_ch4.html +++ b/frontend/textbooks/geometry_11_ch4.html @@ -400,7 +400,7 @@ function buildParaSelector(){ } const BUILT=new Set(); -const BUILDERS = { p8:()=>buildP8(), p9:()=>buildP9(), p10:()=>buildStub('p10'), p11:()=>buildStub('p11'), final4:()=>buildStub('final4') }; +const BUILDERS = { p8:()=>buildP8(), p9:()=>buildP9(), p10:()=>buildP10(), p11:()=>buildStub('p11'), final4:()=>buildStub('final4') }; function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } } function goTo(id){ STATE.current=id; ensureBuilt(id); @@ -1191,6 +1191,422 @@ function buildP9(){ wireReadBtn('p9'); } +/* ===== §10 «Координаты и векторы» ===== */ + +function buildP10(){ + const box = document.getElementById('p10-body'); + if(!box) return; + let html = ''; + + /* === ТЕОРИЯ === */ + + html += makeCard('theory', 'Координаты в пространстве и длина вектора', '§ 10.1', + '
Декартова система координат. Три взаимно перпендикулярные оси $Ox$, $Oy$, $Oz$ с общим началом $O$. Каждая точка пространства задаётся тройкой координат $(x;\\, y;\\, z)$.
' + + 'Вектор $\\vec{v}$ в пространстве — упорядоченная тройка координат:
' + + '$\\vec{v} = (x;\\, y;\\, z)$
' + + 'Длина вектора:
' + + '$|\\vec{v}| = \\sqrt{x^2 + y^2 + z^2}$
' + + 'Расстояние между точками $A(x_1, y_1, z_1)$ и $B(x_2, y_2, z_2)$:
' + + '$|AB| = \\sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2 + (z_2 - z_1)^2}$
' + + 'Координаты середины отрезка $AB$:
' + + '$M = \\left(\\dfrac{x_1+x_2}{2};\\, \\dfrac{y_1+y_2}{2};\\, \\dfrac{z_1+z_2}{2}\\right)$
' + + '$|AB| = \\sqrt{(4-1)^2 + (6-2)^2 + (7-3)^2} = \\sqrt{9 + 16 + 16} = \\sqrt{41} \\approx 6{,}40$.
' + + 'Середина: $M = \\left(\\dfrac{5}{2};\\, 4;\\, 5\\right) = (2{,}5;\\, 4;\\, 5)$.
' + + 'Операции с векторами (поэлементно):
' + + 'Скалярное произведение (две эквивалентные формулы):
' + + '$\\vec{a} \\cdot \\vec{b} = a_1 b_1 + a_2 b_2 + a_3 b_3 = |\\vec{a}| \\cdot |\\vec{b}| \\cdot \\cos\\alpha$
' + + 'Косинус угла между векторами:
' + + '$\\cos\\alpha = \\dfrac{\\vec{a} \\cdot \\vec{b}}{|\\vec{a}| \\cdot |\\vec{b}|}$
' + + 'Условие перпендикулярности: $\\vec{a} \\perp \\vec{b} \\Leftrightarrow \\vec{a} \\cdot \\vec{b} = 0$.
' + + '$\\vec{a} \\cdot \\vec{b} = 1\\cdot 2 + 2\\cdot(-1) + 2\\cdot 2 = 2 - 2 + 4 = 4$.
' + + '$|\\vec{a}| = \\sqrt{1 + 4 + 4} = 3$, $|\\vec{b}| = \\sqrt{4 + 1 + 4} = 3$.
' + + '$\\cos\\alpha = \\dfrac{4}{3 \\cdot 3} = \\dfrac{4}{9} \\approx 0{,}444$, откуда $\\alpha \\approx 63{,}6°$.
' + + '$\\vec{a}$ = ('+fmt(a.x)+'; '+fmt(a.y)+'; '+fmt(a.z)+'), $\\vec{b}$ = ('+fmt(b.x)+'; '+fmt(b.y)+'; '+fmt(b.z)+')
'; + h += '$|\\vec{a}| = \\sqrt{'+fmt(a.x*a.x)+'+'+fmt(a.y*a.y)+'+'+fmt(a.z*a.z)+'} \\approx '+la.toFixed(3)+'$, $|\\vec{b}| \\approx '+lb.toFixed(3)+'$
'; + h += '$\\vec{a} \\cdot \\vec{b} = '+fmt(a.x)+'\\cdot '+fmt(b.x)+' + '+fmt(a.y)+'\\cdot '+fmt(b.y)+' + '+fmt(a.z)+'\\cdot '+fmt(b.z)+' = '+dot.toFixed(3)+'$
'; + if(cosA !== null){ + h += '$\\cos\\alpha = \\dfrac{'+dot.toFixed(3)+'}{'+la.toFixed(3)+'\\cdot '+lb.toFixed(3)+'} \\approx '+cosA.toFixed(3)+'$, $\\alpha \\approx '+ang.toFixed(1)+'°$
'; + } else { + h += 'Один из векторов нулевой — угол не определён.
'; + } + if(perp && la > 1e-6 && lb > 1e-6){ + h += '$\\vec{a} \\perp \\vec{b}$ ✓ (скалярное произведение равно 0)
'; + } + out.innerHTML = h; + renderMath(out); + } + + function bumpSeen(){ + const key = [a.x,a.y,a.z,b.x,b.y,b.z].map(v=>v.toFixed(1)).join('|'); + seen.add(key); + const cnt = document.getElementById('p10-iv1-cnt'); + if(cnt) cnt.textContent = Math.min(seen.size, 4); + if(seen.size >= 4 && !xpGiven){ + xpGiven = true; + addXp(10, 'p10-iv1'); + bumpProgress('p10', 20); + } + } + + function bindSlider(id, axis, key){ + const el = document.getElementById('p10-iv1-'+id); + const lbl = document.getElementById('p10-iv1-'+id+'-v'); + if(!el || !lbl) return; + el.addEventListener('input', function(){ + const v = parseFloat(el.value); + lbl.textContent = (v === Math.round(v)) ? String(v) : v.toFixed(1); + if(axis === 'a') a[key] = v; else b[key] = v; + draw(); updateOut(); bumpSeen(); + }); + } + bindSlider('a1','a','x'); bindSlider('a2','a','y'); bindSlider('a3','a','z'); + bindSlider('b1','b','x'); bindSlider('b2','b','y'); bindSlider('b3','b','z'); + + draw(); updateOut(); + G3D.attachOrbit(svg, scene, draw); + + document.querySelectorAll('#p10-iv1 .g3d-tools .btn').forEach(function(btn){ + btn.addEventListener('click', function(){ + G3D.presetView(scene, btn.dataset.view, draw); + }); + }); + })(); + + /* === IV2 — Калькулятор векторов === */ + (function(){ + const out = document.getElementById('p10-iv2-out'); + const cntEl = document.getElementById('p10-iv2-cnt'); + const used = new Set(); + let xpGiven = false; + + function rd(id){ + const raw = (document.getElementById('p10-iv2-'+id).value || '').replace(',', '.').trim(); + return parseFloat(raw); + } + function readVec(p){ + return { x:rd(p+'1'), y:rd(p+'2'), z:rd(p+'3') }; + } + function vecOk(v){ return isFinite(v.x) && isFinite(v.y) && isFinite(v.z); } + function vecStr(v){ return '('+fmt(v.x)+';\\, '+fmt(v.y)+';\\, '+fmt(v.z)+')'; } + + document.querySelectorAll('#p10-iv2 button[data-op]').forEach(function(btn){ + btn.addEventListener('click', function(){ + const a = readVec('a'), b = readVec('b'); + if(!vecOk(a) || !vecOk(b)){ + out.innerHTML = 'Введи числовые координаты во все поля.'; + return; + } + const op = btn.dataset.op; + let h = ''; + if(op === 'sum'){ + const r = { x:a.x+b.x, y:a.y+b.y, z:a.z+b.z }; + h = '$\\vec{a} + \\vec{b} = ('+fmt(a.x)+'+'+fmt(b.x)+';\\, '+fmt(a.y)+'+'+fmt(b.y)+';\\, '+fmt(a.z)+'+'+fmt(b.z)+') = '+vecStr(r)+'$
'; + } else if(op === 'diff'){ + const r = { x:a.x-b.x, y:a.y-b.y, z:a.z-b.z }; + h = '$\\vec{a} - \\vec{b} = ('+fmt(a.x)+'-('+fmt(b.x)+');\\, '+fmt(a.y)+'-('+fmt(b.y)+');\\, '+fmt(a.z)+'-('+fmt(b.z)+')) = '+vecStr(r)+'$
'; + } else if(op === 'dot'){ + const d = a.x*b.x + a.y*b.y + a.z*b.z; + h = '$\\vec{a} \\cdot \\vec{b} = '+fmt(a.x)+'\\cdot '+fmt(b.x)+' + '+fmt(a.y)+'\\cdot '+fmt(b.y)+' + '+fmt(a.z)+'\\cdot '+fmt(b.z)+' = '+d.toFixed(3)+'$
'; + if(Math.abs(d) < 1e-6) h += '$\\vec{a} \\perp \\vec{b}$ ✓
'; + } else if(op === 'cos'){ + const d = a.x*b.x + a.y*b.y + a.z*b.z; + const la = Math.sqrt(a.x*a.x + a.y*a.y + a.z*a.z); + const lb = Math.sqrt(b.x*b.x + b.y*b.y + b.z*b.z); + if(la < 1e-9 || lb < 1e-9){ h = 'Один из векторов нулевой — угол не определён.
'; } + else { + const c = d/(la*lb); + const ang = Math.acos(Math.max(-1,Math.min(1,c))) * 180/Math.PI; + h = '$|\\vec{a}| \\approx '+la.toFixed(3)+'$, $|\\vec{b}| \\approx '+lb.toFixed(3)+'$, $\\vec{a}\\cdot\\vec{b} = '+d.toFixed(3)+'$
' + + '$\\cos\\alpha = \\dfrac{'+d.toFixed(3)+'}{'+la.toFixed(3)+'\\cdot '+lb.toFixed(3)+'} \\approx '+c.toFixed(3)+'$, $\\alpha \\approx '+ang.toFixed(2)+'°$
'; + } + } else if(op === 'len'){ + const la = Math.sqrt(a.x*a.x + a.y*a.y + a.z*a.z); + h = '$|\\vec{a}| = \\sqrt{'+fmt(a.x*a.x)+'+'+fmt(a.y*a.y)+'+'+fmt(a.z*a.z)+'} = \\sqrt{'+(a.x*a.x+a.y*a.y+a.z*a.z).toFixed(3)+'} \\approx '+la.toFixed(3)+'$
'; + } + out.innerHTML = h; + renderMath(out); + used.add(op); + cntEl.textContent = Math.min(used.size, 4); + if(used.size >= 4 && !xpGiven){ + xpGiven = true; + addXp(15, 'p10-iv2'); + bumpProgress('p10', 30); + } + }); + }); + })(); + + /* === IV3 — Тренажёр === */ + (function(){ + const tasks = [ + { q:'Найди длину вектора $\\vec{a} = (2,\\, 3,\\, 6)$.', a:7, tol:0.05 }, + { q:'Скалярное произведение $\\vec{a} = (1,\\, 2,\\, 3)$ и $\\vec{b} = (4,\\, 5,\\, 6)$.', a:32, tol:0.05 }, + { q:'Расстояние между точками $A(1, 2, 3)$ и $B(4, 6, 7)$.', a:6.40, tol:0.05 }, + { q:'Если $\\vec{a} = (3,\\, 0,\\, 4)$, найди $|\\vec{a}|$.', a:5, tol:0.05 }, + { q:'Найди $\\cos\\alpha$ между $\\vec{a} = (1,\\, 0,\\, 0)$ и $\\vec{b} = (1,\\, 1,\\, 0)$.', a:0.71, tol:0.05 } + ]; + const list = document.getElementById('p10-iv3-list'); + const scoreEl = document.getElementById('p10-iv3-score'); + const solved = new Set(); + let xpGiven = false; + + list.innerHTML = tasks.map(function(t, i){ + return '