diff --git a/frontend/textbooks/geometry_9_ch3.html b/frontend/textbooks/geometry_9_ch3.html index 593241d..6bb0f09 100644 --- a/frontend/textbooks/geometry_9_ch3.html +++ b/frontend/textbooks/geometry_9_ch3.html @@ -103,6 +103,27 @@ a{color:inherit;text-decoration:none} .feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)} .feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)} +.wg{background:linear-gradient(135deg,var(--card),var(--pri-soft));border:1.5px solid var(--pri);border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1} +.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px} +.wg-badge{padding:4px 9px;background:var(--pri);color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em} +.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--pri2);flex:1} +.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg),var(--pri-soft));border-left:4px solid var(--warn);padding:9px 14px;border-radius:9px} +.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s;font-family:'JetBrains Mono',monospace} +.tinp:focus{outline:0;border-color:var(--pri);box-shadow:0 0 0 3px var(--pri-soft)} +.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px} +.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px} +.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5} +.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--pri2);margin-left:4px} +.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--pri)} +.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--pri-soft);border-radius:10px;margin-bottom:12px} +.score-display b{color:var(--pri2);font-size:1.15rem} +.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden} +.spoiler summary{padding:8px 14px;background:var(--pri-soft);font-weight:700;cursor:pointer;font-size:.88rem;color:var(--pri2);list-style:none;display:flex;align-items:center;gap:8px} +.spoiler summary::-webkit-details-marker{display:none} +.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--pri);width:18px} +.spoiler[open] summary::before{content:'\2212'} +.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6} + .col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto} .sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)} .sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)} @@ -420,6 +441,28 @@ function wireReadBtn(paraId){ }); } +/* ===== SVG helpers ===== */ +function rightAngleMark(V, uIn, wIn, s){ + s = s || 9; + const p1 = {x: V.x + s*uIn.x, y: V.y + s*uIn.y}; + const c = {x: p1.x + s*wIn.x, y: p1.y + s*wIn.y}; + const p2 = {x: V.x + s*wIn.x, y: V.y + s*wIn.y}; + return p1.x+','+p1.y+' '+c.x+','+c.y+' '+p2.x+','+p2.y; +} +function angleArcAuto(V, uA, uB, R){ + const sA = {x: V.x + R*uA.x, y: V.y + R*uA.y}; + const eB = {x: V.x + R*uB.x, y: V.y + R*uB.y}; + const cross = uA.x*uB.y - uA.y*uB.x; + const sweep = cross > 0 ? 1 : 0; + return 'M'+sA.x+','+sA.y+' A'+R+','+R+' 0 0,'+sweep+' '+eB.x+','+eB.y; +} +function unitVec(p1, p2){ + const dx = p2.x - p1.x, dy = p2.y - p1.y; + const len = Math.sqrt(dx*dx + dy*dy) || 1; + return {x: dx/len, y: dy/len}; +} +function deg2rad(d){ return d * Math.PI / 180; } + /* ===== STUB BUILDERS — наполнение в Phase 7+ ===== */ function _stubBuilder(paraId, num, name, prev, next){ @@ -435,8 +478,580 @@ function _stubBuilder(paraId, num, name, prev, next){ if(window.renderMathInElement) renderMath(body); } -function buildP10(){ _stubBuilder('p10', '§10', 'Теорема синусов', null, 'p11'); } -function buildP11(){ _stubBuilder('p11', '§11', 'Теорема косинусов', 'p10', 'p12'); } +/* ===== §10 — Теорема синусов ===== */ +function buildP10(){ + const box = document.getElementById('p10-body'); + let html = ''; + + html += makeCard('theory', 'Формулировка теоремы', '10.1', ` +

В любом треугольнике стороны пропорциональны синусам противолежащих углов:

+ $$\\dfrac{a}{\\sin A} = \\dfrac{b}{\\sin B} = \\dfrac{c}{\\sin C} = 2R$$ +

где $a, b, c$ — стороны треугольника, $A, B, C$ — противолежащие им углы, а $R$ — радиус описанной окружности.

+
Почему именно $2R$?
+ Идея доказательства: в треугольнике, вписанном в окружность радиуса $R$, сторона $a$ — это хорда, стягиваемая центральным углом $2A$ (по теореме о вписанном угле). Длина такой хорды равна $2R \\sin A$, откуда $\\dfrac{a}{\\sin A} = 2R$. То же для $b$ и $c$. +
`); + + html += makeCard('rule', 'Радиус описанной окружности', '10.2', ` +

Из теоремы синусов сразу получаем формулы для радиуса описанной окружности:

+ $$R = \\dfrac{a}{2 \\sin A} = \\dfrac{b}{2 \\sin B} = \\dfrac{c}{2 \\sin C} = \\dfrac{abc}{4S}$$ +

А также — выражение стороны через радиус и противолежащий угол:

+ $$a = 2R \\sin A, \\qquad b = 2R \\sin B, \\qquad c = 2R \\sin C$$ +

Это очень удобно: зная всего один угол и противолежащую ему сторону, можно сразу найти $R$ — а через него и любую другую сторону.

`); + + html += makeCard('example', 'Применение', '10.3', ` +

Что можно вычислить?

+ +

Пример. $a = 10$, $A = 30°$, $B = 45°$.

+ $$b = a \\cdot \\dfrac{\\sin B}{\\sin A} = 10 \\cdot \\dfrac{\\sin 45°}{\\sin 30°} = 10 \\cdot \\dfrac{\\sqrt{2}/2}{1/2} = 10\\sqrt{2} \\approx 14{,}14$$ +

Радиус описанной окружности: $R = \\dfrac{a}{2 \\sin A} = \\dfrac{10}{2 \\cdot 0{,}5} = 10$.

`); + + /* IV1 — Треугольник с описанной окружностью */ + html += `
+
ИНТЕРАКТИВ 1
Треугольник в описанной окружности
+
Меняй углы $A$ и $B$ — программа найдёт $C = 180° - A - B$ и стороны $a = 2R\\sin A$, $b = 2R\\sin B$, $c = 2R\\sin C$. Радиус $R = 130$ фиксирован.
+
+ + +
+
+ +
+
+
`; + + /* IV2 — Калькулятор */ + html += `
+
ИНТЕРАКТИВ 2
Калькулятор $R$ и сторон
+
Введи сторону $a$ и два угла $A$, $B$ — программа найдёт $C$, $R$, $b$ и $c$ по теореме синусов.
+
+ + + +
+
+
+
+
`; + + /* IV3 — Что вычислить? */ + html += `
+
ИНТЕРАКТИВ 3
Какую формулу применить?
+
Дано условие — выбери формулу теоремы синусов, которая поможет найти искомое.
+
Задача 1 / 6Очки: 0 / 6
+
+
+ + + +
+
+
`; + + /* IV4 — Тренажёр */ + html += `
+
ИНТЕРАКТИВ 4
Тренажёр теоремы синусов
+
Реши задачу и введи число (округляй до 2 знаков после запятой).
+
Задача 1 / 6Очки: 0 / 6
+
+
+ ответ = + + + +
+
+
`; + + html += readButton('p10'); + html += secNav(null, 'p11'); + + box.innerHTML = html; + renderMath(box); + + /* IV1 — Треугольник в описанной окружности */ + (function(){ + const sA=document.getElementById('p10-iv1-a'); + const sB=document.getElementById('p10-iv1-b'); + const lA=document.getElementById('p10-iv1-aval'); + const lB=document.getElementById('p10-iv1-bval'); + const svg=document.getElementById('p10-iv1-svg'); + const out=document.getElementById('p10-iv1-out'); + const seen=new Set(); + const cx=210, cy=200, R=130; + function draw(){ + let A=+sA.value, B=+sB.value; + // ограничение чтобы сумма не превышала 170 + if(A+B>170){ if(A>B) A=170-B; else B=170-A; } + sA.value=A; sB.value=B; + const C=180-A-B; + lA.textContent=A; lB.textContent=B; + // строим треугольник, вписанный в окружность с углами A, B, C + // выбираем для удобства: вершина CA на угле 210° (центральном) от центра + // Используем: положение вершин на окружности — углы относительно центра 2A, 2B, 2C + // Простой способ: разместим точку A_v внизу слева, B_v внизу справа, C_v вверху + // Углы при центре: arc BC = 2A, arc CA = 2B, arc AB = 2C + // Начнём с A_v на угле theta0=210°, пойдём по окружности + const tA=deg2rad(210); + const tB=tA + deg2rad(2*C); // от A через дугу AB (=2C) до B + const tC=tB + deg2rad(2*A); // от B через дугу BC (=2A) до C + const Av={x:cx+R*Math.cos(tA), y:cy+R*Math.sin(tA)}; + const Bv={x:cx+R*Math.cos(tB), y:cy+R*Math.sin(tB)}; + const Cv={x:cx+R*Math.cos(tC), y:cy+R*Math.sin(tC)}; + const a=Math.hypot(Bv.x-Cv.x,Bv.y-Cv.y); + const b=Math.hypot(Av.x-Cv.x,Av.y-Cv.y); + const c=Math.hypot(Av.x-Bv.x,Av.y-Bv.y); + let s=''; + s += ''; + s += ''; + s += ''; + s += 'O'; + // радиус к одной вершине (показать R) + s += ''; + const mx=(cx+Av.x)/2, my=(cy+Av.y)/2; + s += 'R'; + // треугольник + s += ''; + // вершины и подписи + function lab(P, name, color){ + const ux=(P.x-cx)/R, uy=(P.y-cy)/R; + const lx=P.x+ux*18, ly=P.y+uy*18; + return '' + +''+name+''; + } + s += lab(Av,'A','#0f172a'); + s += lab(Bv,'B','#0f172a'); + s += lab(Cv,'C','#0f172a'); + svg.innerHTML=s; + + const sinA=Math.sin(deg2rad(A))||1e-9; + const ratio = 2*R; // в наших экранных единицах + out.innerHTML = '$A = '+A+'°$, $B = '+B+'°$, $C = '+C+'°$
' + + '$\\dfrac{a}{\\sin A} = \\dfrac{b}{\\sin B} = \\dfrac{c}{\\sin C} = 2R = '+(2*R).toFixed(0)+'$ (в пикселях SVG)
' + + '$a \\approx '+a.toFixed(1)+'$, $b \\approx '+b.toFixed(1)+'$, $c \\approx '+c.toFixed(1)+'$  ·  $a/\\sin A \\approx '+(a/sinA).toFixed(1)+'$'; + renderMath(out); + seen.add(A+'|'+B); + if(seen.size>=4 && !seen.has('done')){ addXp(10,'p10-iv1'); bumpProgress('p10',15); seen.add('done'); } + } + [sA,sB].forEach(s=>s.addEventListener('input', draw)); + draw(); + })(); + + /* IV2 — Калькулятор */ + (function(){ + const aI=document.getElementById('p10-iv2-a'); + const AI=document.getElementById('p10-iv2-A'); + const BI=document.getElementById('p10-iv2-B'); + const go=document.getElementById('p10-iv2-go'); + const out=document.getElementById('p10-iv2-out'); + const fb=document.getElementById('p10-iv2-fb'); + let solved=0; + function calc(){ + const a=parseFloat(aI.value), A=parseFloat(AI.value), B=parseFloat(BI.value); + if(!isFinite(a)||!isFinite(A)||!isFinite(B)){ feedback(fb,false,'✗ Введи все значения.'); return; } + if(a<=0){ feedback(fb,false,'✗ Сторона должна быть положительной.'); return; } + if(A<=0||B<=0||A+B>=180){ feedback(fb,false,'✗ Углы должны быть положительными, сумма $A + B < 180°$.'); return; } + const C=180-A-B; + const sinA=Math.sin(deg2rad(A)), sinB=Math.sin(deg2rad(B)), sinC=Math.sin(deg2rad(C)); + const R=a/(2*sinA); + const b=2*R*sinB; + const c=2*R*sinC; + out.innerHTML = '$C = 180° - A - B = '+C.toFixed(1)+'°$
' + + '$R = \\dfrac{a}{2 \\sin A} = \\dfrac{'+a+'}{2 \\sin '+A+'°} \\approx '+R.toFixed(3)+'$
' + + '$b = 2R \\sin B = 2 \\cdot '+R.toFixed(3)+' \\cdot \\sin '+B+'° \\approx '+b.toFixed(3)+'$
' + + '$c = 2R \\sin C = 2 \\cdot '+R.toFixed(3)+' \\cdot \\sin '+C.toFixed(1)+'° \\approx '+c.toFixed(3)+'$'; + renderMath(out); + feedback(fb,true,'✓ Готово!'); + solved++; + if(solved===1){ addXp(10,'p10-iv2'); bumpProgress('p10',10); } + } + go.addEventListener('click', calc); + })(); + + /* IV3 — «Какую формулу применить?» */ + (function(){ + const Q=[ + {t:'Известны $a$ и $A$. Найти радиус $R$ описанной окружности.', a:'R'}, + {t:'Известны $R$ и угол $A$. Найти сторону $a$.', a:'side'}, + {t:'Известны $a$, $A$ и угол $B$. Найти сторону $b$.', a:'prop'}, + {t:'Известны $R$ и угол $B$. Найти сторону $b$.', a:'side'}, + {t:'Известны $b$, $B$ и угол $C$. Найти сторону $c$.', a:'prop'}, + {t:'Известны сторона $c$ и угол $C$. Найти радиус $R$.', a:'R'} + ]; + const explain={ + R:'Прямо $R = \\dfrac{a}{2\\sin A}$ — сторону пропускаем через формулу $2R$.', + side:'Сторона через радиус: $a = 2R \\sin A$.', + prop:'Связываем две стороны через их углы: $\\dfrac{a}{\\sin A} = \\dfrac{b}{\\sin B}$.' + }; + const labels={R:'$\\dfrac{a}{\\sin A} = 2R$', side:'$a = 2R\\sin A$', prop:'$\\dfrac{a}{\\sin A} = \\dfrac{b}{\\sin B}$'}; + const qBox=document.getElementById('p10-iv3-q'); + const iEl=document.getElementById('p10-iv3-i'); + const sEl=document.getElementById('p10-iv3-s'); + const fb=document.getElementById('p10-iv3-fb'); + const btns=document.querySelectorAll('#p10-iv3 button[data-ans]'); + let i=0, score=0, done=false; + function show(){ + if(i>=Q.length){ + qBox.innerHTML='Завершено! Очки: '+score+' / '+Q.length; + if(!done){ done=true; addXp(15,'p10-iv3'); bumpProgress('p10',25); if(score===Q.length) achievement('p10_done'); } + btns.forEach(b=>b.disabled=true); + return; + } + qBox.innerHTML=Q[i].t; renderMath(qBox); + iEl.textContent=(i+1); sEl.textContent=score; + fb.style.display='none'; + } + btns.forEach(b=>b.addEventListener('click', ()=>{ + const ok = b.dataset.ans===Q[i].a; + if(ok) score++; + feedback(fb, ok, ok?('✓ Верно! '+explain[Q[i].a]):('✗ Правильная формула: '+labels[Q[i].a]+'. '+explain[Q[i].a])); + i++; + setTimeout(show, 1100); + })); + show(); + })(); + + /* IV4 — Тренажёр */ + (function(){ + const Q=[ + {t:'$a = 10$, $A = 30°$. Найди радиус $R$ описанной окружности.', a:10, tol:0.02}, + {t:'$R = 5$, $A = 90°$. Найди сторону $a$.', a:10, tol:0.02}, + {t:'$a = 8$, $A = 45°$, $B = 60°$. Найди сторону $b$.', a:9.80, tol:0.05}, + {t:'В прямоугольном треугольнике гипотенуза $c = 10$, угол $C = 90°$. Найди $R$.', a:5, tol:0.02}, + {t:'$a = 6$, $A = 30°$, $B = 60°$. Найди сторону $b$.', a:10.39, tol:0.05}, + {t:'$b = 12$, $B = 60°$. Найди радиус $R$.', a:6.93, tol:0.05} + ]; + const qBox=document.getElementById('p10-iv4-q'); + const ans=document.getElementById('p10-iv4-ans'); + const go=document.getElementById('p10-iv4-go'); + const reset=document.getElementById('p10-iv4-start'); + const iEl=document.getElementById('p10-iv4-i'); + const sEl=document.getElementById('p10-iv4-s'); + const fb=document.getElementById('p10-iv4-fb'); + let i=0, score=0, done=false; + function show(){ + if(i>=Q.length){ + qBox.innerHTML='Финиш! Очки: '+score+' / '+Q.length; + if(!done){ done=true; addXp(15,'p10-iv4'); bumpProgress('p10',25); } + go.disabled=true; ans.disabled=true; + return; + } + qBox.innerHTML=Q[i].t; renderMath(qBox); + iEl.textContent=(i+1); sEl.textContent=score; + ans.value=''; fb.style.display='none'; go.disabled=false; ans.disabled=false; + } + go.addEventListener('click', ()=>{ + const v=parseFloat((ans.value||'').replace(',', '.')); + if(!isFinite(v)){ feedback(fb,false,'✗ Введи число.'); return; } + const tol=Q[i].tol||0.05; + const ok=Math.abs(v-Q[i].a)<=tol; + if(ok) score++; + feedback(fb, ok, ok?'✓ Верно!':'✗ Правильный ответ: $'+Q[i].a+'$'); + i++; + setTimeout(show, 1000); + }); + reset.addEventListener('click', ()=>{ i=0; score=0; done=false; show(); }); + show(); + })(); + + wireReadBtn('p10'); +} + +/* ===== §11 — Теорема косинусов ===== */ +function buildP11(){ + const box = document.getElementById('p11-body'); + let html = ''; + + html += makeCard('theory', 'Формулировка теоремы', '11.1', ` +

В любом треугольнике квадрат стороны равен сумме квадратов двух других сторон минус удвоенное произведение этих сторон на косинус угла между ними:

+ $$a^2 = b^2 + c^2 - 2bc \\cos A$$ +

Аналогично — для двух других сторон:

+ $$b^2 = a^2 + c^2 - 2ac \\cos B, \\qquad c^2 = a^2 + b^2 - 2ab \\cos C$$ +

Это обобщение теоремы Пифагора: при $A = 90°$ имеем $\\cos A = 0$, и формула превращается в $a^2 = b^2 + c^2$.

+
Когда нужна теорема косинусов?
+ Если известны две стороны и угол между ними — третья сторона находится теоремой косинусов. Если известны все три стороны — любой угол находится из неё же. В этих двух случаях теорема синусов не помогает (нет пары «сторона + противолежащий угол»). +
`); + + html += makeCard('rule', 'Применение для нахождения сторон и углов', '11.2', ` +

Случай 1: известны две стороны $b, c$ и угол $A$ между ними. Тогда

+ $$a = \\sqrt{b^2 + c^2 - 2bc \\cos A}$$ +

Случай 2: известны все три стороны $a, b, c$. Тогда любой угол находится по формуле:

+ $$\\cos A = \\dfrac{b^2 + c^2 - a^2}{2bc}, \\qquad \\cos B = \\dfrac{a^2 + c^2 - b^2}{2ac}, \\qquad \\cos C = \\dfrac{a^2 + b^2 - c^2}{2ab}$$ +

Затем $A = \\arccos(\\cos A)$.

+

Удобно знать: если $\\cos A > 0$ — угол $A$ острый, если $\\cos A < 0$ — тупой, если $\\cos A = 0$ — прямой.

`); + + html += makeCard('example', 'Примеры', '11.3', ` +

Пример 1. Две стороны $b = 5$, $c = 7$, угол между ними $A = 60°$. Найти третью сторону.

+ $$a^2 = 25 + 49 - 2 \\cdot 5 \\cdot 7 \\cdot \\cos 60° = 74 - 70 \\cdot 0{,}5 = 39$$ + $$a = \\sqrt{39} \\approx 6{,}24$$ +

Пример 2. Стороны $3, 4, 5$. Найти угол между сторонами $3$ и $4$ (т.е. противолежащий стороне $5$).

+ $$\\cos C = \\dfrac{9 + 16 - 25}{2 \\cdot 3 \\cdot 4} = 0 \\Rightarrow C = 90°$$ +

Это знакомый прямоугольный треугольник 3–4–5.

+

Пример 3. Стороны $7, 8, 13$. Найти наибольший угол (он лежит против $13$).

+ $$\\cos A = \\dfrac{49 + 64 - 169}{2 \\cdot 7 \\cdot 8} = \\dfrac{-56}{112} = -0{,}5 \\Rightarrow A = 120°$$`); + + /* IV1 — Slider угла → одна сторона */ + html += `
+
ИНТЕРАКТИВ 1
Угол меняется — сторона тоже
+
Стороны $b = 100$ и $c = 150$ зафиксированы. Меняй угол $A$ между ними — третья сторона $a$ вычисляется по теореме косинусов.
+
+ +
+
+ +
+
+
`; + + /* IV2 — Калькулятор */ + html += `
+
ИНТЕРАКТИВ 2
Калькулятор стороны и угла
+
Две формы: одна находит сторону по двум сторонам и углу, другая — угол по трём сторонам.
+
Найти сторону $a$
+
+ + + +
+
+
+ +
Найти угол $A$
+
+ + + +
+
+
+ +
`; + + /* IV3 — Что найти? */ + html += `
+
ИНТЕРАКТИВ 3
Что искать — сторону или угол?
+
Дано условие — выбери, в каком виде применить теорему косинусов: для нахождения стороны или для нахождения угла.
+
Задача 1 / 6Очки: 0 / 6
+
+
+ + +
+ +
`; + + /* IV4 — Тренажёр */ + html += `
+
ИНТЕРАКТИВ 4
Тренажёр теоремы косинусов
+
Реши задачу и введи число (округляй до 2 знаков после запятой).
+
Задача 1 / 6Очки: 0 / 6
+
+
+ ответ = + + + +
+ +
`; + + html += readButton('p11'); + html += secNav('p10', 'p12'); + + box.innerHTML = html; + renderMath(box); + + /* IV1 — Slider угла A */ + (function(){ + const sA=document.getElementById('p11-iv1-a'); + const lA=document.getElementById('p11-iv1-aval'); + const svg=document.getElementById('p11-iv1-svg'); + const out=document.getElementById('p11-iv1-out'); + const seen=new Set(); + const b=100, c=150; + function draw(){ + const A=+sA.value; lA.textContent=A; + // A_v в позиции (90, 240), B_v = A_v + (c, 0) = (240, 240). C_v под углом A от A_v на расстояние b. + const Av={x:90, y:240}; + const Bv={x:Av.x+c, y:Av.y}; + const Cv={x:Av.x+b*Math.cos(deg2rad(A)), y:Av.y-b*Math.sin(deg2rad(A))}; + const a=Math.hypot(Bv.x-Cv.x, Bv.y-Cv.y); + let s=''; + s += ''; + // треугольник + s += ''; + // дуга угла A + const uAB=unitVec(Av,Bv), uAC=unitVec(Av,Cv); + s += ''; + // прямой угол, если A=90 + if(Math.abs(A-90)<0.5){ + s += ''; + } + // подписи сторон + const midAB={x:(Av.x+Bv.x)/2, y:(Av.y+Bv.y)/2}; + const midAC={x:(Av.x+Cv.x)/2, y:(Av.y+Cv.y)/2}; + const midBC={x:(Bv.x+Cv.x)/2, y:(Bv.y+Cv.y)/2}; + s += 'c = '+c+''; + s += 'b = '+b+''; + s += 'a = '+a.toFixed(1)+''; + // подпись угла A + s += 'A = '+A+'°'; + // вершины + [['A',Av],['B',Bv],['C',Cv]].forEach(([n,P])=>{ + s += ''; + const dx=(n==='A'?-12:(n==='B'?12:0)), dy=(n==='C'?-10:18); + s += ''+n+''; + }); + svg.innerHTML=s; + + const cosA=Math.cos(deg2rad(A)); + const a2 = b*b + c*c - 2*b*c*cosA; + let info = '$a^2 = b^2 + c^2 - 2bc \\cos A = '+(b*b)+' + '+(c*c)+' - '+(2*b*c)+' \\cdot \\cos '+A+'° \\approx '+a2.toFixed(1)+'$
' + + '$a \\approx '+Math.sqrt(a2).toFixed(2)+'$'; + if(Math.abs(A-90)<0.5) info += '
✓ Прямой угол — это теорема Пифагора!'; + out.innerHTML=info; + renderMath(out); + seen.add(A); + if(seen.size>=4 && !seen.has('done')){ addXp(10,'p11-iv1'); bumpProgress('p11',15); seen.add('done'); } + } + sA.addEventListener('input', draw); + draw(); + })(); + + /* IV2 — Калькулятор */ + (function(){ + const bI=document.getElementById('p11-iv2-b'); + const cI=document.getElementById('p11-iv2-c'); + const AI=document.getElementById('p11-iv2-A'); + const goS=document.getElementById('p11-iv2-goS'); + const outS=document.getElementById('p11-iv2-outS'); + const aI=document.getElementById('p11-iv2-a2'); + const b2I=document.getElementById('p11-iv2-b2'); + const c2I=document.getElementById('p11-iv2-c2'); + const goA=document.getElementById('p11-iv2-goA'); + const outA=document.getElementById('p11-iv2-outA'); + const fb=document.getElementById('p11-iv2-fb'); + let solvedS=0, solvedA=0; + goS.addEventListener('click', ()=>{ + const b=parseFloat(bI.value), c=parseFloat(cI.value), A=parseFloat(AI.value); + if(!isFinite(b)||!isFinite(c)||!isFinite(A)){ feedback(fb,false,'✗ Введи все значения.'); return; } + if(b<=0||c<=0){ feedback(fb,false,'✗ Стороны должны быть положительными.'); return; } + if(A<=0||A>=180){ feedback(fb,false,'✗ Угол $A$ от $0°$ до $180°$.'); return; } + const a2 = b*b + c*c - 2*b*c*Math.cos(deg2rad(A)); + const a = Math.sqrt(a2); + outS.innerHTML = '$a^2 = b^2 + c^2 - 2bc \\cos A = '+(b*b).toFixed(2)+' + '+(c*c).toFixed(2)+' - '+(2*b*c).toFixed(2)+' \\cdot \\cos '+A+'° \\approx '+a2.toFixed(3)+'$
' + + '$a \\approx '+a.toFixed(3)+'$'; + renderMath(outS); + feedback(fb,true,'✓ Сторона найдена.'); + solvedS++; if(solvedS+solvedA===1){ addXp(10,'p11-iv2'); bumpProgress('p11',10); } + }); + goA.addEventListener('click', ()=>{ + const a=parseFloat(aI.value), b=parseFloat(b2I.value), c=parseFloat(c2I.value); + if(!isFinite(a)||!isFinite(b)||!isFinite(c)){ feedback(fb,false,'✗ Введи все значения.'); return; } + if(a<=0||b<=0||c<=0){ feedback(fb,false,'✗ Стороны должны быть положительными.'); return; } + // проверка неравенства треугольника + if(a+b<=c || a+c<=b || b+c<=a){ feedback(fb,false,'✗ Неравенство треугольника не выполняется.'); return; } + let cosA = (b*b + c*c - a*a)/(2*b*c); + if(cosA>1) cosA=1; if(cosA<-1) cosA=-1; + const A = Math.acos(cosA) * 180 / Math.PI; + outA.innerHTML = '$\\cos A = \\dfrac{b^2 + c^2 - a^2}{2bc} = \\dfrac{'+(b*b).toFixed(2)+' + '+(c*c).toFixed(2)+' - '+(a*a).toFixed(2)+'}{'+(2*b*c).toFixed(2)+'} \\approx '+cosA.toFixed(4)+'$
' + + '$A = \\arccos('+cosA.toFixed(4)+') \\approx '+A.toFixed(2)+'°$'; + renderMath(outA); + feedback(fb,true,'✓ Угол найден.'); + solvedA++; if(solvedS+solvedA===1){ addXp(10,'p11-iv2'); bumpProgress('p11',10); } + }); + })(); + + /* IV3 — «Что искать?» */ + (function(){ + const Q=[ + {t:'Известны 2 стороны и угол между ними. Найти третью сторону.', a:'side'}, + {t:'Известны 3 стороны. Найти один из углов.', a:'angle'}, + {t:'Стороны $b = 5$, $c = 7$, угол $A = 60°$. Найти сторону $a$.', a:'side'}, + {t:'Стороны $a = 13$, $b = 14$, $c = 15$. Найти угол $B$.', a:'angle'}, + {t:'Стороны $b = 8$, $c = 10$, угол $A = 120°$. Найти сторону $a$.', a:'side'}, + {t:'Стороны треугольника $5, 12, 13$. Найти наибольший угол.', a:'angle'} + ]; + const explain={ + side:'$a^2 = b^2 + c^2 - 2bc \\cos A$ — даёт квадрат искомой стороны.', + angle:'$\\cos A = \\dfrac{b^2 + c^2 - a^2}{2bc}$ — даёт косинус искомого угла.' + }; + const labels={side:'для стороны', angle:'для угла'}; + const qBox=document.getElementById('p11-iv3-q'); + const iEl=document.getElementById('p11-iv3-i'); + const sEl=document.getElementById('p11-iv3-s'); + const fb=document.getElementById('p11-iv3-fb'); + const btns=document.querySelectorAll('#p11-iv3 button[data-ans]'); + let i=0, score=0, done=false; + function show(){ + if(i>=Q.length){ + qBox.innerHTML='Завершено! Очки: '+score+' / '+Q.length; + if(!done){ done=true; addXp(15,'p11-iv3'); bumpProgress('p11',25); if(score===Q.length) achievement('p11_done'); } + btns.forEach(b=>b.disabled=true); + return; + } + qBox.innerHTML=Q[i].t; renderMath(qBox); + iEl.textContent=(i+1); sEl.textContent=score; + fb.style.display='none'; + } + btns.forEach(b=>b.addEventListener('click', ()=>{ + const ok = b.dataset.ans===Q[i].a; + if(ok) score++; + feedback(fb, ok, ok?('✓ Верно! '+explain[Q[i].a]):('✗ Правильно: '+labels[Q[i].a]+'. '+explain[Q[i].a])); + i++; + setTimeout(show, 1100); + })); + show(); + })(); + + /* IV4 — Тренажёр */ + (function(){ + const Q=[ + {t:'Стороны $b = 5$, $c = 8$, угол $A = 60°$. Найди $a$.', a:7, tol:0.02}, + {t:'Стороны $b = 3$, $c = 4$, угол $A = 90°$. Найди $a$.', a:5, tol:0.02}, + {t:'Стороны треугольника $7, 8, 13$. Найди наибольший угол (в градусах).', a:120, tol:0.5}, + {t:'Стороны $b = 6$, $c = 10$, угол $A = 120°$. Найди $a$.', a:14, tol:0.02}, + {t:'Стороны $3, 5, 7$. Найди угол против стороны $7$ (в градусах).', a:120, tol:0.5}, + {t:'Стороны $b = 4$, $c = 6$, угол $A = 45°$. Найди $a^2$.', a:18.06, tol:0.1} + ]; + const qBox=document.getElementById('p11-iv4-q'); + const ans=document.getElementById('p11-iv4-ans'); + const go=document.getElementById('p11-iv4-go'); + const reset=document.getElementById('p11-iv4-start'); + const iEl=document.getElementById('p11-iv4-i'); + const sEl=document.getElementById('p11-iv4-s'); + const fb=document.getElementById('p11-iv4-fb'); + let i=0, score=0, done=false; + function show(){ + if(i>=Q.length){ + qBox.innerHTML='Финиш! Очки: '+score+' / '+Q.length; + if(!done){ done=true; addXp(15,'p11-iv4'); bumpProgress('p11',25); } + go.disabled=true; ans.disabled=true; + return; + } + qBox.innerHTML=Q[i].t; renderMath(qBox); + iEl.textContent=(i+1); sEl.textContent=score; + ans.value=''; fb.style.display='none'; go.disabled=false; ans.disabled=false; + } + go.addEventListener('click', ()=>{ + const v=parseFloat((ans.value||'').replace(',', '.')); + if(!isFinite(v)){ feedback(fb,false,'✗ Введи число.'); return; } + const tol=Q[i].tol||0.05; + const ok=Math.abs(v-Q[i].a)<=tol; + if(ok) score++; + feedback(fb, ok, ok?'✓ Верно!':'✗ Правильный ответ: $'+Q[i].a+'$'); + i++; + setTimeout(show, 1000); + }); + reset.addEventListener('click', ()=>{ i=0; score=0; done=false; show(); }); + show(); + })(); + + wireReadBtn('p11'); +} function buildP12(){ _stubBuilder('p12', '§12', 'Формула Герона. Решение треугольников', 'p11', 'final3'); } function buildFinal3(){