From 74793b0616cbf6ff4a13368553d1e2d058baf130 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 29 May 2026 09:57:26 +0300 Subject: [PATCH] =?UTF-8?q?feat(geom9=20ch2=20wave1):=20=C2=A77=20=C2=AB?= =?UTF-8?q?=D0=9E=D0=BA=D1=80=D1=83=D0=B6=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20?= =?UTF-8?q?=D1=82=D1=80=D0=B5=D1=83=D0=B3=D0=BE=D0=BB=D1=8C=D0=BD=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=C2=BB=20+=20=C2=A78=20=C2=AB=D0=9E=D0=BA=D1=80?= =?UTF-8?q?=D1=83=D0=B6=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20=D0=BF=D1=80=D1=8F?= =?UTF-8?q?=D0=BC=D0=BE=D1=83=D0=B3=D0=BE=D0=BB=D1=8C=D0=BD=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/textbooks/geometry_9_ch2.html | 616 ++++++++++++++++++++++++- 1 file changed, 614 insertions(+), 2 deletions(-) diff --git a/frontend/textbooks/geometry_9_ch2.html b/frontend/textbooks/geometry_9_ch2.html index b147d91..50689b7 100644 --- a/frontend/textbooks/geometry_9_ch2.html +++ b/frontend/textbooks/geometry_9_ch2.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,29 @@ 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; } +function gcd(a,b){ a=Math.abs(a|0); b=Math.abs(b|0); while(b){ const t=b; b=a%b; a=t; } return a||1; } + /* ===== STUB BUILDERS — наполнение в Phase 7+ ===== */ function _stubBuilder(paraId, num, name, prev, next){ @@ -435,8 +479,576 @@ function _stubBuilder(paraId, num, name, prev, next){ if(window.renderMathInElement) renderMath(body); } -function buildP7(){ _stubBuilder('p7', '§7', 'Описанная и вписанная окружности треугольника', null, 'p8'); } -function buildP8(){ _stubBuilder('p8', '§8', 'Окружности прямоугольного треугольника', 'p7', 'p9'); } +/* ===== §7 Описанная и вписанная окружности треугольника ===== */ +function buildP7(){ + const box = document.getElementById('p7-body'); + let html = ''; + + html += makeCard('theory', 'Описанная окружность', '7.1', ` +

Описанная около треугольника окружность проходит через все три вершины. Её центр — точка пересечения серединных перпендикуляров к сторонам, обозначается $O$. Радиус $R$.

+

Для любого треугольника существует и единственна описанная окружность.

+
Почему серединные перпендикуляры?
+ Серединный перпендикуляр к отрезку — множество точек, равноудалённых от его концов. Точка $O$ равноудалена от $A$ и $B$ (на серединном перпендикуляре к $AB$) и от $B$ и $C$ (на серединном перпендикуляре к $BC$), значит $OA = OB = OC = R$. +
`); + + html += makeCard('rule', 'Вписанная окружность', '7.2', ` +

Вписанная в треугольник окружность касается всех трёх сторон изнутри. Её центр — точка пересечения биссектрис углов, обозначается $I$. Радиус $r$.

+

Также существует и единственна. Формула связи площади и вписанного радиуса:

+ $$S_\\triangle = p \\cdot r$$ +

где $p = \\dfrac{a+b+c}{2}$ — полупериметр.

+

Радиус описанной выражается через стороны и площадь:

+ $$R = \\dfrac{abc}{4S}, \\qquad r = \\dfrac{S}{p}$$`); + + html += makeCard('example', 'Замечательные свойства', '7.3', ` + +

Пример. В треугольнике 3-4-5 (прямоугольном): $S = 6$, $p = 6$, $r = S/p = 1$, $R = abc/(4S) = 60/24 = 2{,}5$ — половина гипотенузы.

`); + + /* IV1 — Описанная и вписанная окружности */ + html += `
+
ИНТЕРАКТИВ 1
Описанная и вписанная окружности
+
Выбери тип треугольника — увидишь его описанную ($O$, синяя) и вписанную ($I$, оранжевая) окружности. Заметь, где располагается центр $O$ в каждом случае.
+
+ +
+
+ +
+
+
`; + + /* IV2 — Калькулятор R и r */ + html += `
+
ИНТЕРАКТИВ 2
Калькулятор $R$ и $r$
+
Введи три стороны треугольника $a$, $b$, $c$ — программа проверит неравенство треугольника и найдёт радиусы по формулам Герона: $S = \\sqrt{p(p-a)(p-b)(p-c)}$, $R = \\tfrac{abc}{4S}$, $r = \\tfrac{S}{p}$.
+
+ $a$ = + + $b$ = + + $c$ = + + +
+
+ +
`; + + /* IV3 — Quickfire «Внутри или снаружи?» */ + html += `
+
ИНТЕРАКТИВ 3
Внутри или снаружи?
+
Дан треугольник — определи, где находится центр $O$ описанной окружности.
+
Задача 1 / 6Очки: 0 / 6
+
+
+ + + +
+ +
`; + + /* IV4 — Тренажёр */ + html += `
+
ИНТЕРАКТИВ 4
Тренажёр $R$ и $r$
+
Реши задачу и введи число (округли до 2 знаков, если получается дробное).
+
Задача 1 / 6Очки: 0 / 6
+
+
+ ответ = + + + +
+ +
`; + + html += secNav(null, 'p8'); + html += readButton('p7'); + + box.innerHTML = html; + renderMath(box); + + /* IV1 — визуализатор окружностей */ + (function(){ + const sl = document.getElementById('p7-iv1-t'); + const lab = document.getElementById('p7-iv1-tval'); + const svg = document.getElementById('p7-iv1-svg'); + const out = document.getElementById('p7-iv1-out'); + const seen = new Set(); + // 5 пресетов: углы A, B, C + const PRESETS = [ + {name:'Равносторонний', angles:[60,60,60], kind:'in'}, + {name:'Равнобедренный 70-70-40', angles:[70,70,40], kind:'in'}, + {name:'Прямоугольный 30-60-90', angles:[30,60,90], kind:'hyp'}, + {name:'Произвольный 40-60-80', angles:[40,60,80], kind:'in'}, + {name:'Тупоугольный 30-30-120', angles:[30,30,120], kind:'out'} + ]; + function draw(){ + const idx = (+sl.value) - 1; + lab.textContent = (idx+1); + const P = PRESETS[idx]; + const [Ad, Bd, Cd] = P.angles; + const aR = deg2rad(Ad), bR = deg2rad(Bd); + // Сторона BC = a, CA = b, AB = c (закон синусов: a/sinA = 2R) + // Зададим масштаб через 2R = 180 (картинка ~ 360 широкая после) + const twoR = 170; + const a = twoR * Math.sin(aR); + const b = twoR * Math.sin(bR); + const c = twoR * Math.sin(deg2rad(Cd)); + // Положим B в (xB, yB) и C в (xB+a, yB) — горизонтально. + // Тогда A находится в позиции с углом B при вершине B и c — сторона BA. + const cx0 = 210, cy0 = 230; // центр SVG + const Bx = -a/2, By = 0; + const Cx = a/2, Cy = 0; + // A: длина BA = c, угол при B = B (между BA и BC), в SVG y вниз — поднимем A выше + const Ax = Bx + c * Math.cos(bR); + const Ay = By - c * Math.sin(bR); + // Сдвиг так, чтобы центрировать + const cxAll = (Ax+Bx+Cx)/3, cyAll = (Ay+By+Cy)/3; + const A = {x: cx0 + (Ax - cxAll), y: cy0 + (Ay - cyAll)}; + const B = {x: cx0 + (Bx - cxAll), y: cy0 + (By - cyAll)}; + const C = {x: cx0 + (Cx - cxAll), y: cy0 + (Cy - cyAll)}; + // Длины сторон (в SVG пикселях) + function dist(P,Q){ return Math.hypot(P.x-Q.x, P.y-Q.y); } + const sa = dist(B,C), sb = dist(C,A), sc = dist(A,B); + const p = (sa+sb+sc)/2; + const S = Math.sqrt(Math.max(0, p*(p-sa)*(p-sb)*(p-sc))); + const R = (sa*sb*sc)/(4*S); + const r = S / p; + // Центр описанной (через формулы): O = пересечение серединных перпендикуляров. + const D = 2 * (A.x*(B.y-C.y) + B.x*(C.y-A.y) + C.x*(A.y-B.y)); + const Ox = ((A.x*A.x+A.y*A.y)*(B.y-C.y) + (B.x*B.x+B.y*B.y)*(C.y-A.y) + (C.x*C.x+C.y*C.y)*(A.y-B.y)) / D; + const Oy = ((A.x*A.x+A.y*A.y)*(C.x-B.x) + (B.x*B.x+B.y*B.y)*(A.x-C.x) + (C.x*C.x+C.y*C.y)*(B.x-A.x)) / D; + // Инцентр: I = (sa·A + sb·B + sc·C) / (sa+sb+sc) + const Ix = (sa*A.x + sb*B.x + sc*C.x) / (sa+sb+sc); + const Iy = (sa*A.y + sb*B.y + sc*C.y) / (sa+sb+sc); + + let s = ''; + s += ''; + // Описанная окружность + s += ''; + // Вписанная окружность + s += ''; + // Треугольник + s += ''; + // Центры + s += ''; + s += 'O'; + s += ''; + s += 'I'; + // Вершины + s += ''; + s += ''; + s += ''; + s += 'A'; + s += 'B'; + s += 'C'; + // Подпись + s += ''+P.name+''; + svg.innerHTML = s; + + const place = P.kind==='in' ? 'внутри треугольника' : (P.kind==='hyp' ? 'на гипотенузе' : 'снаружи треугольника'); + out.innerHTML = 'Углы: '+Ad+'°, '+Bd+'°, '+Cd+'°  ·  $R \\approx '+R.toFixed(1)+'$  ·  $r \\approx '+r.toFixed(1)+'$
Центр $O$ находится '+place+'.'; + renderMath(out); + seen.add(idx); + if(seen.size>=4 && !seen.has('done')){ addXp(10,'p7-iv1'); bumpProgress('p7',15); seen.add('done'); } + } + sl.addEventListener('input', draw); + draw(); + })(); + + /* IV2 — Калькулятор */ + (function(){ + const aI=document.getElementById('p7-iv2-a'); + const bI=document.getElementById('p7-iv2-b'); + const cI=document.getElementById('p7-iv2-c'); + const go=document.getElementById('p7-iv2-go'); + const out=document.getElementById('p7-iv2-out'); + const fb=document.getElementById('p7-iv2-fb'); + let solved=0; + function calc(){ + const a=parseFloat(aI.value), b=parseFloat(bI.value), c=parseFloat(cI.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,'✗ Неравенство треугольника не выполняется: сумма любых двух сторон должна быть больше третьей.'); out.innerHTML=''; return; } + const p=(a+b+c)/2; + const S=Math.sqrt(p*(p-a)*(p-b)*(p-c)); + const R=(a*b*c)/(4*S); + const r=S/p; + out.innerHTML='$p = \\tfrac{'+a+'+'+b+'+'+c+'}{2} = '+fmt(p)+'$
' + + '$S = \\sqrt{'+fmt(p)+' \\cdot '+fmt(p-a)+' \\cdot '+fmt(p-b)+' \\cdot '+fmt(p-c)+'} \\approx '+S.toFixed(3)+'$
' + + '$R = \\dfrac{abc}{4S} = \\dfrac{'+(a*b*c)+'}{'+fmt(4*S)+'} \\approx '+R.toFixed(3)+'$
' + + '$r = \\dfrac{S}{p} \\approx '+r.toFixed(3)+'$'; + renderMath(out); + feedback(fb,true,'✓ Готово!'); + solved++; + if(solved===1){ addXp(10,'p7-iv2'); bumpProgress('p7',15); } + } + go.addEventListener('click', calc); + })(); + + /* IV3 — Quickfire */ + (function(){ + const Q=[ + {t:'Остроугольный треугольник (все углы меньше 90°). Где центр $O$?', a:'in'}, + {t:'Прямоугольный треугольник. Где центр $O$?', a:'hyp'}, + {t:'Тупоугольный треугольник (один угол больше 90°). Где центр $O$?', a:'out'}, + {t:'Равносторонний треугольник. Где центр $O$?', a:'in'}, + {t:'Треугольник с углами $90°, 45°, 45°$. Где центр $O$?', a:'hyp'}, + {t:'Треугольник с углами $30°, 30°, 120°$. Где центр $O$?', a:'out'} + ]; + const qBox=document.getElementById('p7-iv3-q'); + const iEl=document.getElementById('p7-iv3-i'); + const sEl=document.getElementById('p7-iv3-s'); + const fb=document.getElementById('p7-iv3-fb'); + const btns=document.querySelectorAll('#p7-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,'p7-iv3'); bumpProgress('p7',25); if(score===Q.length) achievement('p7_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?'✓ Верно!':'✗ Правильный ответ: '+ ({in:'Внутри',hyp:'На гипотенузе',out:'Снаружи'}[Q[i].a])); + i++; + setTimeout(show, 700); + })); + show(); + })(); + + /* IV4 — Тренажёр */ + (function(){ + const Q=[ + {t:'В треугольнике стороны $3,4,5$. Найди $R$.', a:2.5}, + {t:'В треугольнике стороны $3,4,5$. Найди $r$.', a:1}, + {t:'В равностороннем треугольнике со стороной $6$ найди $R$ (округли до 2 знаков).', a:3.46}, + {t:'В равностороннем треугольнике со стороной $6$ найди $r$ (округли до 2 знаков).', a:1.73}, + {t:'В треугольнике стороны $5,12,13$. Найди $R$.', a:6.5}, + {t:'В треугольнике стороны $6,8,10$. Найди $r$.', a:2} + ]; + const qBox=document.getElementById('p7-iv4-q'); + const ans=document.getElementById('p7-iv4-ans'); + const go=document.getElementById('p7-iv4-go'); + const reset=document.getElementById('p7-iv4-start'); + const iEl=document.getElementById('p7-iv4-i'); + const sEl=document.getElementById('p7-iv4-s'); + const fb=document.getElementById('p7-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,'p7-iv4'); bumpProgress('p7',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); + if(!isFinite(v)){ feedback(fb,false,'✗ Введи число.'); return; } + const ok=Math.abs(v-Q[i].a)<0.05; + if(ok) score++; + feedback(fb, ok, ok?'✓ Верно!':'✗ Правильный ответ: $'+Q[i].a+'$'); + i++; + setTimeout(show, 900); + }); + reset.addEventListener('click', ()=>{ i=0; score=0; done=false; show(); }); + show(); + })(); + + wireReadBtn('p7'); +} + +/* ===== §8 Окружности прямоугольного треугольника ===== */ +function buildP8(){ + const box = document.getElementById('p8-body'); + let html = ''; + + html += makeCard('theory', 'Описанная окружность', '8.1', ` +

В прямоугольном треугольнике центр описанной окружности — середина гипотенузы. Радиус:

+ $$R = \\dfrac{c}{2}$$ +

где $c$ — гипотенуза. Это следствие теоремы о вписанном угле: вписанный угол, опирающийся на диаметр, равен $90°$.

+

Гипотенуза — диаметр описанной окружности!

+
Почему именно середина гипотенузы?
+ В прямоугольном треугольнике медиана к гипотенузе равна её половине. Точка $O$ — середина гипотенузы — равноудалена от трёх вершин на $c/2$, значит это центр описанной окружности. +
`); + + html += makeCard('rule', 'Вписанная окружность', '8.2', ` +

В прямоугольном треугольнике с катетами $a, b$ и гипотенузой $c$ радиус вписанной окружности:

+ $$r = \\dfrac{a + b - c}{2}$$ +

Также верно: $r = \\dfrac{a \\cdot b}{a + b + c}$ (через площадь $S = \\tfrac{ab}{2}$ и полупериметр $p = \\tfrac{a+b+c}{2}$, $r = S/p$).

`); + + html += makeCard('example', 'Примеры применения', '8.3', ` +
    +
  • В треугольнике 3-4-5: $R = 5/2 = 2{,}5$, $r = (3+4-5)/2 = 1$. Проверка: $S = 6, p = 6, r = S/p = 1$. Сошлось.
  • +
  • В треугольнике 5-12-13: $R = 13/2 = 6{,}5$, $r = (5+12-13)/2 = 2$.
  • +
  • В треугольнике 8-15-17: $R = 17/2 = 8{,}5$, $r = (8+15-17)/2 = 3$.
  • +
`); + + /* IV1 — SVG */ + html += `
+
ИНТЕРАКТИВ 1
Окружности прямоугольного треугольника
+
Меняй катеты ползунками — гипотенуза, $R$ и $r$ пересчитаются. Заметь: центр описанной — посередине гипотенузы.
+
+ + +
+
+ +
+
+
`; + + /* IV2 — Калькулятор */ + html += `
+
ИНТЕРАКТИВ 2
Калькулятор $R$ и $r$
+
Введи катеты $a$ и $b$ — программа найдёт гипотенузу, радиусы окружностей и проверит $r = S/p$.
+
+ $a$ = + + $b$ = + + +
+
+ +
`; + + /* IV3 — «Сравни формулы» */ + html += `
+
ИНТЕРАКТИВ 3
Верно или ошибка?
+
Утверждение о прямоугольном треугольнике — оцени, верно ли оно.
+
Задача 1 / 6Очки: 0 / 6
+
+
+ + +
+ +
`; + + /* IV4 — Тренажёр */ + html += `
+
ИНТЕРАКТИВ 4
Тренажёр
+
Реши задачу и введи число.
+
Задача 1 / 6Очки: 0 / 6
+
+
+ ответ = + + + +
+ +
`; + + html += secNav('p7', 'p9'); + html += readButton('p8'); + + box.innerHTML = html; + renderMath(box); + + /* IV1 */ + (function(){ + const aS=document.getElementById('p8-iv1-a'); + const bS=document.getElementById('p8-iv1-b'); + const aL=document.getElementById('p8-iv1-aval'); + const bL=document.getElementById('p8-iv1-bval'); + const svg=document.getElementById('p8-iv1-svg'); + const out=document.getElementById('p8-iv1-out'); + const seen=new Set(); + function draw(){ + const a=+aS.value, b=+bS.value; + aL.textContent=a; bL.textContent=b; + // Вершины: C = прямой угол (нижний-левый), B = (a, 0) — катет a по горизонтали, A = (0, -b) + const margin=40; + const C={x:margin, y:margin+b}; + const B={x:margin+a, y:margin+b}; + const A={x:margin, y:margin}; + const c=Math.hypot(a,b); + const R=c/2; + const r=(a+b-c)/2; + // Центр описанной — середина гипотенузы AB + const O={x:(A.x+B.x)/2, y:(A.y+B.y)/2}; + // Центр вписанной: на (r, r) от вершины прямого угла внутрь. + // C — нижний-левый; внутрь треугольника направление: +x (к B), -y (к A) + const I={x:C.x+r, y:C.y-r}; + const uCA=unitVec(C,A); + const uCB=unitVec(C,B); + + let s=''; + s += ''; + // Описанная окружность + s += ''; + // Вписанная + s += ''; + // Треугольник + s += ''; + // Прямой угол в C + s += ''; + // Центры + s += ''; + s += 'O'; + s += ''; + s += 'I'; + // Вершины + подписи + s += ''; + s += ''; + s += ''; + s += 'A'; + s += 'B'; + s += 'C'; + // Подписи сторон + s += 'a='+a+''; + s += 'b='+b+''; + // Гипотенуза + const midAB={x:(A.x+B.x)/2, y:(A.y+B.y)/2}; + s += 'c='+c.toFixed(1)+''; + svg.innerHTML = s; + + out.innerHTML = '$c = \\sqrt{'+(a*a)+'+'+(b*b)+'} \\approx '+c.toFixed(2)+'$  ·  $R = c/2 \\approx '+R.toFixed(2)+'$  ·  $r = (a+b-c)/2 \\approx '+r.toFixed(2)+'$
Диаметр описанной = гипотенуза $c$.'; + renderMath(out); + seen.add(a+'-'+b); + if(seen.size>=4 && !seen.has('done')){ addXp(10,'p8-iv1'); bumpProgress('p8',15); seen.add('done'); } + } + aS.addEventListener('input', draw); + bS.addEventListener('input', draw); + draw(); + })(); + + /* IV2 */ + (function(){ + const aI=document.getElementById('p8-iv2-a'); + const bI=document.getElementById('p8-iv2-b'); + const go=document.getElementById('p8-iv2-go'); + const out=document.getElementById('p8-iv2-out'); + const fb=document.getElementById('p8-iv2-fb'); + let solved=0; + function calc(){ + const a=parseFloat(aI.value), b=parseFloat(bI.value); + if(!isFinite(a)||!isFinite(b)){ feedback(fb,false,'✗ Введи два числа.'); return; } + if(a<=0||b<=0){ feedback(fb,false,'✗ Катеты должны быть положительными.'); return; } + const c=Math.sqrt(a*a+b*b); + const R=c/2; + const r=(a+b-c)/2; + const S=a*b/2; + const p=(a+b+c)/2; + const rCheck=S/p; + out.innerHTML='$c = \\sqrt{'+a+'^2+'+b+'^2} = \\sqrt{'+(a*a+b*b)+'} \\approx '+c.toFixed(3)+'$
' + +'$R = \\dfrac{c}{2} \\approx '+R.toFixed(3)+'$
' + +'$r = \\dfrac{a+b-c}{2} \\approx '+r.toFixed(3)+'$
' + +'Проверка: $S = \\tfrac{ab}{2} = '+fmt(S)+'$, $p = '+fmt(p)+'$, $r = S/p \\approx '+rCheck.toFixed(3)+'$'; + renderMath(out); + feedback(fb,true,'✓ Готово!'); + solved++; + if(solved===1){ addXp(10,'p8-iv2'); bumpProgress('p8',15); } + } + go.addEventListener('click', calc); + })(); + + /* IV3 — Quickfire верно/ошибка */ + (function(){ + const Q=[ + {t:'$R = \\dfrac{c}{2}$, где $c$ — гипотенуза.', a:'1'}, + {t:'$r = a + b - c$ (без деления на 2).', a:'0'}, + {t:'Гипотенуза — диаметр описанной окружности.', a:'1'}, + {t:'Центр описанной окружности лежит вне прямоугольного треугольника.', a:'0'}, + {t:'$r = \\dfrac{a+b+c}{2}$.', a:'0'}, + {t:'В треугольнике 6-8-10 радиус описанной окружности равен $5$.', a:'1'} + ]; + const fix=['✗ $r = \\dfrac{a+b-c}{2}$ — обязательно делить на 2.', + '', + '', + '✗ Центр $O$ лежит на середине гипотенузы (на самой гипотенузе).', + '✗ Это полупериметр $p$, а не радиус. $r = \\dfrac{a+b-c}{2}$.', + '']; + const qBox=document.getElementById('p8-iv3-q'); + const iEl=document.getElementById('p8-iv3-i'); + const sEl=document.getElementById('p8-iv3-s'); + const fb=document.getElementById('p8-iv3-fb'); + const btns=document.querySelectorAll('#p8-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,'p8-iv3'); bumpProgress('p8',25); if(score===Q.length) achievement('p8_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++; + const correctMsg = Q[i].a==='1' ? '✓ Верно — утверждение истинно.' : (fix[i] || '✓ Верно — утверждение ложно.'); + feedback(fb, ok, ok?correctMsg:('✗ '+(Q[i].a==='1'?'На самом деле — верно.':(fix[i]||'На самом деле — ошибка.')))); + i++; + setTimeout(show, 900); + })); + show(); + })(); + + /* IV4 */ + (function(){ + const Q=[ + {t:'В треугольнике $3,4,5$ найди $R$.', a:2.5}, + {t:'В треугольнике $5,12,13$ найди $r$.', a:2}, + {t:'В треугольнике $8,15,17$ найди $R$.', a:8.5}, + {t:'В треугольнике $8,15,17$ найди $r$.', a:3}, + {t:'В треугольнике $6,8,10$ найди $r$.', a:2}, + {t:'В треугольнике $9,40,41$ найди $R$.', a:20.5} + ]; + const qBox=document.getElementById('p8-iv4-q'); + const ans=document.getElementById('p8-iv4-ans'); + const go=document.getElementById('p8-iv4-go'); + const reset=document.getElementById('p8-iv4-start'); + const iEl=document.getElementById('p8-iv4-i'); + const sEl=document.getElementById('p8-iv4-s'); + const fb=document.getElementById('p8-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,'p8-iv4'); bumpProgress('p8',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); + if(!isFinite(v)){ feedback(fb,false,'✗ Введи число.'); return; } + const ok=Math.abs(v-Q[i].a)<0.05; + if(ok) score++; + feedback(fb, ok, ok?'✓ Верно!':'✗ Правильный ответ: $'+Q[i].a+'$'); + i++; + setTimeout(show, 900); + }); + reset.addEventListener('click', ()=>{ i=0; score=0; done=false; show(); }); + show(); + })(); + + wireReadBtn('p8'); +} function buildP9(){ _stubBuilder('p9', '§9', 'Вписанные и описанные четырёхугольники', 'p8', 'final2'); } function buildFinal2(){