From ecda85e8ef6997ad475b2d7ff9f5eacdb92c9f27 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Wed, 27 May 2026 20:18:47 +0300 Subject: [PATCH] =?UTF-8?q?feat(geom8):=20Wave=203=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B2=D1=8B=201=20=E2=80=94=20=C2=A711-=C2=A716=20(=D0=A4?= =?UTF-8?q?=D0=B0=D0=BB=D0=B5=D1=81,=20=D0=BC=D0=B5=D0=B4=D0=B8=D0=B0?= =?UTF-8?q?=D0=BD=D1=8B,=20=D1=81=D1=80=D0=B5=D0=B4=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BB=D0=B8=D0=BD=D0=B8=D0=B8,=20=D1=82=D1=80=D0=B0=D0=BF?= =?UTF-8?q?=D0=B5=D1=86=D0=B8=D1=8F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §11 Теорема Фалеса: SVG-угол с параллелями, конструктор деления отрезка на n частей, тренажёр, DnD, босс. §12 Медианы: SVG-треугольник drag + центроид G, доказательство 2:1 через среднюю линию, калькулятор, тренажёр, босс. §13 Средняя линия треугольника: SVG со срединным треугольником, доказательство, mini-quiz, DnD, тренажёр, босс. §14 Трапеция: SVG drag (сохраняет параллельность оснований), конструктор типов, доказательство m=(a+b)/2, калькулятор, тренажёр, босс. §15 Равнобедренная трапеция: SVG с симметрией, 2 доказательства (углы, диагонали), DnD свойств, тренажёр, босс. §16 Признаки равнобедренной: 2 SVG-индикатора, доказательство признака, mini-quiz, тренажёр, босс. GLOSSARY: +центроид, +основания трапеции. File: 3373 → 5194 LOC. Co-Authored-By: Claude Opus 4.7 (1M context) --- frontend/textbooks/geometry_8_ch1.html | 1837 +++++++++++++++++++++++- 1 file changed, 1829 insertions(+), 8 deletions(-) diff --git a/frontend/textbooks/geometry_8_ch1.html b/frontend/textbooks/geometry_8_ch1.html index ddd52df..8c89a11 100644 --- a/frontend/textbooks/geometry_8_ch1.html +++ b/frontend/textbooks/geometry_8_ch1.html @@ -493,8 +493,8 @@ const BUILT = new Set(); const BUILDERS = { p1:()=>buildP1(), p2:()=>buildP2(), p3:()=>buildP3(), p4:()=>buildP4(), p5:()=>buildP5(), p6:()=>buildP6(), p7:()=>buildP7(), p8:()=>buildP8(), - p9:()=>buildP9(), p10:()=>buildP10(), p11:()=>buildP11stub(), p12:()=>buildP12stub(), - p13:()=>buildP13stub(), p14:()=>buildP14stub(), p15:()=>buildP15stub(), p16:()=>buildP16stub(), + p9:()=>buildP9(), p10:()=>buildP10(), p11:()=>buildP11(), p12:()=>buildP12(), + p13:()=>buildP13(), p14:()=>buildP14(), p15:()=>buildP15(), p16:()=>buildP16(), final1:()=>buildFinal1stub(), }; function ensureBuilt(id){ if(BUILT.has(id)) return; const fn = BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } } @@ -660,6 +660,8 @@ const GLOSSARY = [ { term:'средняя линия треугольника', def:'Отрезок, соединяющий середины двух сторон треугольника. Параллелен третьей стороне и равен её половине.', sec:'p13', aliases:['средняя линия треугольника','средней линии треугольника','средние линии треугольника'] }, { term:'средняя линия трапеции', def:'Отрезок, соединяющий середины боковых сторон трапеции. Равен полусумме оснований.', sec:'p14', aliases:['средняя линия трапеции','средней линии трапеции'] }, { term:'медиана', def:'Отрезок, проведённый из вершины треугольника к середине противоположной стороны.', sec:'p12', aliases:['медиана','медианы','медиан','медианам','медиану'] }, + { term:'центроид', def:'Точка пересечения медиан треугольника (центр тяжести). Делит каждую медиану в отношении 2:1 от вершины.', sec:'p12', aliases:['центроид','центроида','центроиде'] }, + { term:'основания трапеции', def:'Параллельные стороны трапеции, обозначаемые обычно a (большее) и b (меньшее основание).', sec:'p14', aliases:['основания трапеции','основаниям трапеции','основание трапеции','основания'] }, { term:'теорема Фалеса', def:'Если параллельные прямые пересекают две прямые, они отсекают на них пропорциональные отрезки.', sec:'p11', aliases:['теорема Фалеса','теореме Фалеса','теоремы Фалеса'] }, { term:'подобные треугольники', def:'Треугольники, у которых углы равны попарно и стороны пропорциональны.', sec:'p11', aliases:['подобные треугольники','подобных треугольников','подобными треугольниками'] }, { term:'касательная', def:'Прямая, имеющая одну общую точку с окружностью и перпендикулярная радиусу в точке касания.', sec:'p11', aliases:['касательная','касательной','касательную','касательных'] }, @@ -3361,12 +3363,1831 @@ function buildP10(){ })(); renderMath(box); } -function buildP11stub(){ document.getElementById('p11-body').innerHTML = '

§11 — Волна 1: содержимое появится в следующем обновлении.

' + secNav('p10','p12'); } -function buildP12stub(){ document.getElementById('p12-body').innerHTML = '

§12 — Волна 1: содержимое появится в следующем обновлении.

' + secNav('p11','p13'); } -function buildP13stub(){ document.getElementById('p13-body').innerHTML = '

§13 — Волна 1: содержимое появится в следующем обновлении.

' + secNav('p12','p14'); } -function buildP14stub(){ document.getElementById('p14-body').innerHTML = '

§14 — Волна 1: содержимое появится в следующем обновлении.

' + secNav('p13','p15'); } -function buildP15stub(){ document.getElementById('p15-body').innerHTML = '

§15 — Волна 1: содержимое появится в следующем обновлении.

' + secNav('p14','p16'); } -function buildP16stub(){ document.getElementById('p16-body').innerHTML = '

§16 — Волна 1: содержимое появится в следующем обновлении.

' + secNav('p15','final1'); } +function buildP11(){ + const box = document.getElementById('p11-body'); + let html = ''; + + html += makeCard('theory','Теорема Фалеса','11.1',` +

Теорема Фалеса. Если на одной стороне угла отложены равные отрезки и через их концы проведены прямые, параллельные другой стороне угла, то эти прямые отсекают на второй стороне угла равные отрезки.

+

Обобщение: если параллельные прямые пересекают две прямые (секущие) и отсекают на одной из них равные отрезки, то и на другой они отсекают равные отрезки.

+

Формально: $a_1\\parallel a_2\\parallel a_3$, $OA_1=A_1A_2 \\Rightarrow OB_1=B_1B_2$.

`); + + html += makeCard('rule','Деление отрезка на n равных частей','11.2',` +

Алгоритм (деление $AB$ на $n$ частей):

+
    +
  1. Из точки $A$ провести произвольный луч $AC$, не совпадающий с $AB$.
  2. +
  3. На луче $AC$ отложить $n$ равных отрезков: $AA_1=A_1A_2=\\ldots=A_{n-1}A_n$.
  4. +
  5. Соединить $A_n$ с $B$.
  6. +
  7. Через $A_1,A_2,\\ldots,A_{n-1}$ провести прямые, параллельные $A_nB$.
  8. +
  9. Точки пересечения с $AB$ — искомые точки деления.
  10. +
`); + + html += makeCard('example','Доказательство теоремы Фалеса','11.3',` +

Через $A_1$ проведём прямую, параллельную $OB$. Она пересекает $A_2B_2$ в точке $C$.

+

$\\triangle OA_1B_1 \\cong \\triangle A_1A_2C$ (два угла и сторона): $\\angle A_1OB_1 = \\angle A_1A_2C$ (параллельные), $OA_1=A_1A_2$ (условие). $\\Rightarrow B_1C = OB_1$.

+

Аналогично $\\triangle A_1B_1C \\cong \\triangle A_2B_2C'$... итог: $OB_1 = B_1B_2$. $\\square$

`); + + /* INTERACTIVE 1: SVG Теорема Фалеса */ + html += `
+
ИНТЕРАКТИВ 1
Живая теорема Фалеса — тащи вершину угла
+
Перетащи вершину O или точку A₁. Отрезки на обеих сторонах угла остаются равными.
+
+
+
+
`; + + /* INTERACTIVE 2: Конструктор деления отрезка */ + html += `
+
ИНТЕРАКТИВ 2
Конструктор: деление отрезка на n частей
+
Выбери число частей и нажми «Построить» — увидишь пошаговое построение.
+
+ ${[2,3,4,5,6,7,8].map(n=>``).join('')} +
+
+
+ + +
+
+
`; + + /* INTERACTIVE 3: Тренажёр */ + html += `
+
ИНТЕРАКТИВ 3
Тренажёр задач на теорему Фалеса
+
Задача 1 / 5Очки: 0
+
+
+ + + +
+ +
`; + + /* INTERACTIVE 4: DnD — равные / не равные отрезки */ + html += `
+
ИНТЕРАКТИВ 4
Какие отрезки равны при данной конфигурации?
+
Три параллельных прямых отсекают равные отрезки на первой секущей. Распредели отрезки по группам.
+ ${DND_HINT_HTML} +
+
+
Равны другим
+
Не обязательно равны
+
+
+ +
`; + + /* INTERACTIVE 5: Босс §11 */ + html += `
+
БОСС §11
Итоговые задачи
+
+5 XP за каждую верную задачу.
+
+
`; + + html += `
+ +
`; + + html += secNav('p10','p12'); + box.innerHTML = html; + + /* == SVG Теорема Фалеса == */ + (function(){ + const W=380, H=300; + let Ox=60, Oy=240, A1x=140, A1y=100; + let nPts=3; + + function drawThales(){ + const n = nPts; + const dx = A1x - Ox, dy = A1y - Oy; + const len1 = Math.hypot(dx, dy); + // unit vector for ray OA + const ux = dx/len1, uy = dy/len1; + // second ray: perpendicular-ish offset + const ang2 = Math.atan2(dy,dx) + 0.55; + const ux2 = Math.cos(ang2), uy2 = Math.sin(ang2); + const step1 = len1 / n; + + // points on ray 1: O, A1, A2, ... An + const ray1 = []; + for(let i=0;i<=n;i++) ray1.push({x: Ox + ux*step1*i, y: Oy + uy*step1*i}); + // points on ray 2 — Thales says spacing = step1 * |ray2_unit_proj| adjusted so we get EQUAL spacing + // We use the same arc-length step for consistent visual: step2 = step1 (Thales guarantees it) + const step2 = step1; + const ray2 = []; + for(let i=0;i<=n;i++) ray2.push({x: Ox + ux2*step2*i, y: Oy + uy2*step2*i}); + + let s = ``; + + // parallel lines through each pair + const colors=['#0891b2','#10b981','#f59e0b','#8b5cf6','#ef4444','#ec4899']; + for(let i=0;i<=n;i++){ + const p1=ray1[i], p2=ray2[i]; + const vx=p2.x-p1.x, vy=p2.y-p1.y; + const ext=200; + const nx=vx/Math.hypot(vx,vy), ny=vy/Math.hypot(vx,vy); + if(i===0){ + // just the vertex + } else { + s+=``; + } + } + + // ray 1 + s+=``; + // ray 2 + s+=``; + + // tick marks and labels on ray1 + for(let i=1;i<=n;i++){ + const p=ray1[i]; + s+=``; + const lx=(p.x+ux*12).toFixed(1), ly=(p.y+uy*12).toFixed(1); + s+=`A${i}`; + } + // tick marks and labels on ray2 + for(let i=1;i<=n;i++){ + const p=ray2[i]; + s+=``; + const lx=(p.x+ux2*12).toFixed(1), ly=(p.y+uy2*12).toFixed(1); + s+=`B${i}`; + } + + // O vertex — draggable + s+=``; + s+=``; + s+=`O`; + + // A1 draggable + s+=``; + s+=``; + + s+=``; + + const wrap = document.getElementById('p11-thales-svg'); + wrap.innerHTML = s; + const svgEl = wrap.querySelector('svg'); + + svgEl.querySelectorAll('.p11-drag').forEach(el=>{ + el.addEventListener('pointerdown', ev=>{ + if(ev.button!==undefined&&ev.button!==0) return; + const vname = el.dataset.v; + try{el.setPointerCapture(ev.pointerId);}catch(e){} + function onMove(e){ + const rect=svgEl.getBoundingClientRect(); + const sx=W/rect.width, sy=H/rect.height; + const nx=(e.clientX-rect.left)*sx, ny=(e.clientY-rect.top)*sy; + if(vname==='O'){ Ox=Math.max(10,Math.min(W-10,nx)); Oy=Math.max(10,Math.min(H-10,ny)); } + else if(vname==='A1'){ A1x=Math.max(10,Math.min(W-10,nx)); A1y=Math.max(10,Math.min(H-10,ny)); } + drawThales(); + } + function onUp(){ el.removeEventListener('pointermove',onMove); el.removeEventListener('pointerup',onUp); el.removeEventListener('pointercancel',onUp); } + el.addEventListener('pointermove',onMove); el.addEventListener('pointerup',onUp); el.addEventListener('pointercancel',onUp); + }); + }); + + const segLen = step1.toFixed(1); + document.getElementById('p11-thales-info').innerHTML = + `Все отрезки на первой стороне равны: OA₁ = A₁A₂ = … = ${segLen}
Теорема Фалеса гарантирует: OB₁ = B₁B₂ = … = ${segLen} на второй стороне.`; + } + + document.getElementById('p11-n-sl').addEventListener('input', function(){ nPts=+this.value; document.getElementById('p11-n-val').textContent=nPts; drawThales(); }); + drawThales(); + })(); + + /* == Конструктор деления == */ + (function(){ + let nDiv=3, animStep=0, animTimer=null; + + function drawDiv(step){ + const W2=400, H2=260; + const Ax=40, Ay=180, Bx=360, By=180; + const rayAng=-0.55; + const rayLen=120; + const ux=Math.cos(rayAng), uy=Math.sin(rayAng); + const segR = rayLen / nDiv; + + let s=``; + + // AB + s+=``; + s+=``; + s+=``; + s+=`A`; + s+=`B`; + + if(step >= 1){ + // auxiliary ray + const Cnx=Ax+ux*rayLen, Cny=Ay+uy*rayLen; + s+=``; + s+=`A${nDiv}`; + } + if(step >= 2){ + // equal segments on ray + for(let i=1;i<=nDiv;i++){ + const px=Ax+ux*segR*i, py=Ay+uy*segR*i; + s+=``; + if(iA${i}`; + } + } + if(step >= 3){ + // line AnB + const Anx=Ax+ux*rayLen, Any=Ay+uy*rayLen; + s+=``; + } + if(step >= 4){ + // parallels through Ai to AB + const Anx=Ax+ux*rayLen, Any=Ay+uy*rayLen; + const vx=Bx-Anx, vy=By-Any; + const colors=['#10b981','#8b5cf6','#ec4899','#f59e0b','#0891b2','#ef4444','#64748b']; + for(let i=1;i`; + s+=``; + // label + const frac=i+'/'+nDiv; + s+=`${frac}`; + } + } + s+=``; + document.getElementById('p11-div-svg').innerHTML=s; + const stepTexts=['','Шаг 1: Из A проведём вспомогательный луч AC.','Шаг 2: На луче откладываем n равных отрезков AA₁=A₁A₂=…','Шаг 3: Соединяем последнюю точку Aₙ с B.','Шаг 4: Через A₁,…,Aₙ₋₁ проводим прямые, параллельные AₙB. Готово — отрезки равны!']; + document.getElementById('p11-div-step').textContent = stepTexts[Math.min(step,4)] || ''; + } + + document.querySelectorAll('.p11-ndiv-btn').forEach(btn=>{ + btn.addEventListener('click',()=>{ + nDiv=+btn.dataset.n; + document.querySelectorAll('.p11-ndiv-btn').forEach(b=>b.className='btn p11-ndiv-btn'); + btn.className='btn primary p11-ndiv-btn'; + animStep=0; if(animTimer){clearInterval(animTimer);animTimer=null;} drawDiv(0); + }); + }); + document.getElementById('p11-div-build').addEventListener('click',()=>{ + if(animTimer) return; + animStep=0; drawDiv(0); + animTimer=setInterval(()=>{ animStep++; drawDiv(animStep); if(animStep>=4){clearInterval(animTimer);animTimer=null;addXp(3,'p11-div');} },900); + }); + document.getElementById('p11-div-reset').addEventListener('click',()=>{ if(animTimer){clearInterval(animTimer);animTimer=null;} animStep=0; drawDiv(0); }); + // activate first button + document.querySelector('.p11-ndiv-btn[data-n="3"]').className='btn primary p11-ndiv-btn'; + drawDiv(0); + })(); + + /* == Тренажёр == */ + (function(){ + const tasks=[ + {q:'Отрезок $AB=20$. Разделить на $4$ равные части. Длина каждой части?', ans:5, hint:'20÷4=5'}, + {q:'На одной стороне угла $OA_1=A_1A_2=3$. На второй — $OB_1=B_1B_2$. Найди $B_1B_2$.', ans:3, hint:'По теореме Фалеса B₁B₂ = A₁A₂ = 3'}, + {q:'Три параллельные прямые отсекают на одной секущей отрезки по $7$. Сколько равных отрезков на второй секущей?', ans:3, hint:'По теореме Фалеса — тоже 3 равных отрезка по 7'}, + {q:'Отрезок $AB=36$. Разделить на $9$ равных частей. Длина каждой части?', ans:4, hint:'36÷9=4'}, + {q:'$OA_1=5$, $A_1A_2=5$, $A_2A_3=5$. Параллели через $A_1,A_2,A_3$ дают на второй стороне $OB_1$. Найди $OB_1$.', ans:5, hint:'OA₁=5, по Фалесу OB₁=5'}, + ]; + let idx=0, score=0; + function show(){ document.getElementById('p11-tr-i').textContent=idx+1; document.getElementById('p11-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p11-tr-task')); document.getElementById('p11-tr-ans').value=''; document.getElementById('p11-tr-fb').style.display='none'; } + document.getElementById('p11-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p11-tr-score').textContent=0;show();}); + document.getElementById('p11-tr-go').addEventListener('click',()=>{ + const ans=+document.getElementById('p11-tr-ans').value; const fb=document.getElementById('p11-tr-fb'); + if(ans===tasks[idx].ans){ score++;document.getElementById('p11-tr-score').textContent=score;addXp(3,'p11-train');bumpProgress('p11',5); if(idxshow(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p11-train-all');} } + else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint); + }); + document.getElementById('p11-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p11-tr-go').click();}); + show(); + })(); + + /* == DnD == */ + (function(){ + const items=[ + {id:'d1',html:'OA₁ и A₁A₂ (на первой стороне, равны по условию)', ans:'eq'}, + {id:'d2',html:'OB₁ и B₁B₂ (на второй стороне, по Фалесу)', ans:'eq'}, + {id:'d3',html:'OA₁ и OB₁ (разные стороны угла)', ans:'neq'}, + {id:'d4',html:'A₁A₂ и B₁B₂ (параллельные секущие)', ans:'eq'}, + {id:'d5',html:'OA₁ и A₂A₃ (на одной стороне, равны)', ans:'eq'}, + {id:'d6',html:'A₁B₁ и A₂B₂ (боковые — не секущие)', ans:'neq'}, + ]; + const sorter=setupSorter({poolId:'p11-dnd-pool',scopeSelector:'#p11-dnd-wrap',items,cats:['eq','neq']}); + document.getElementById('p11-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p11-dnd-fb').style.display='none';}); + document.getElementById('p11-dnd-check').addEventListener('click',()=>{ + let ok=0; items.forEach(it=>{if(sorter.placed[it.id]===it.ans)ok++;}); + const fb=document.getElementById('p11-dnd-fb'); + if(ok===items.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p11-dnd');bumpProgress('p11',15);} + else feedback(fb,false,'Верно: '+ok+' из '+items.length+'.'); + }); + })(); + + /* == Босс §11 == */ + (function(){ + const tasks=[ + {q:'Три параллельных прямых отсекают на первой секущей отрезки $4, 4, 4$. Найди сумму отрезков на второй секущей.', ans:12, hint:'По Фалесу тоже 4+4+4=12'}, + {q:'Отрезок $AB=35$. Разделить на $7$ равных частей. Длина каждой?', ans:5, hint:'35÷7=5'}, + {q:'$OA_1=6$, параллели дают $OB_1$ и $B_1B_2=6$. Найди $B_1B_2$.', ans:6, hint:'По Фалесу B₁B₂ = A₁A₂ = 6'}, + {q:'На одной стороне угла отложены $n=5$ равных отрезков по $3$. Сколько равных отрезков будет на второй стороне?', ans:5, hint:'По Фалесу — тоже 5'}, + ]; + const bossBox=document.getElementById('p11-boss-tasks'); + bossBox.innerHTML=tasks.map((t,i)=>` +
+
${t.q}
+
+ + +
+ +
`).join(''); + window.p11BossSolved=new Set(); + })(); + renderMath(box); +} +function buildP12(){ + const box = document.getElementById('p12-body'); + let html = ''; + + html += makeCard('theory','Медианы треугольника','12.1',` +

Медиана треугольника — отрезок, соединяющий вершину с серединой противоположной стороны.

+

В треугольнике $ABC$: $AM_A$, $BM_B$, $CM_C$ — три медианы, где $M_A$, $M_B$, $M_C$ — середины сторон $BC$, $AC$, $AB$.

`); + + html += makeCard('rule','Свойство медиан','12.2',` +

Теорема. Три медианы треугольника пересекаются в одной точке $G$ (центроид, центр тяжести), которая делит каждую медиану в отношении $2:1$, считая от вершины:

+ \\[AG:GM_A = BG:GM_B = CG:GM_C = 2:1\\] +

Следствие: $AG = \\dfrac{2}{3}AM_A$, $GM_A = \\dfrac{1}{3}AM_A$.

`); + + html += makeCard('example','Формулы для медиан','12.3',` +

Длина медианы $m_a$ (от вершины $A$ к середине $BC$):

+ \\[m_a = \\frac{1}{2}\\sqrt{2b^2+2c^2-a^2}\\] +

где $a,b,c$ — стороны треугольника.

+

Если $G$ делит $AM_A$ в отношении $2:1$: дано $|GM_A|=x \\Rightarrow |AM_A|=3x$, $|AG|=2x$.

`); + + /* INTERACTIVE 1: SVG-треугольник с медианами */ + html += `
+
ИНТЕРАКТИВ 1
Живые медианы — тащи вершины треугольника
+
Перетащи вершины A, B, C. Медианы пересекаются в центроиде G (соотношение 2:1).
+
+
+
`; + + /* INTERACTIVE 2: Доказательство */ + html += `
+
ИНТЕРАКТИВ 2
Доказательство свойства медиан
+
+
+
+ + +
+
`; + + /* INTERACTIVE 3: Калькулятор */ + html += `
+
ИНТЕРАКТИВ 3
Калькулятор: медианы и центроид
+
Введи длину медианы — найди отрезки AG и GM.
+
+ + +
+
+
`; + + /* INTERACTIVE 4: Тренажёр */ + html += `
+
ИНТЕРАКТИВ 4
Тренажёр задач на медианы
+
Задача 1 / 5Очки: 0
+
+
+ + + +
+ +
`; + + /* INTERACTIVE 5: Босс §12 */ + html += `
+
БОСС §12
Итоговые задачи
+
+5 XP за каждую верную задачу.
+
+
`; + + html += `
+ +
`; + + html += secNav('p11','p13'); + box.innerHTML = html; + + /* == SVG медианы == */ + (function(){ + const W=380, H=300; + let A={x:190,y:30}, B={x:50,y:270}, C={x:330,y:270}; + function mid(P,Q){ return {x:(P.x+Q.x)/2, y:(P.y+Q.y)/2}; } + function centroid(A,B,C){ return {x:(A.x+B.x+C.x)/3, y:(A.y+B.y+C.y)/3}; } + function dist(P,Q){ return Math.hypot(Q.x-P.x, Q.y-P.y); } + + function redraw(){ + const Ma=mid(B,C), Mb=mid(A,C), Mc=mid(A,B); + const G=centroid(A,B,C); + const dAG=dist(A,G), dGMa=dist(G,Ma); + const ratio=(dAG/dGMa).toFixed(2); + + let s=``; + // triangle fill + s+=``; + // medians + const mCols=['#10b981','#f59e0b','#8b5cf6']; + [[A,Ma],[B,Mb],[C,Mc]].forEach(([V,M],i)=>{ + s+=``; + // midpoint dot + s+=``; + // midpoint label + const ox=[0,-14,14][i], oy=[14,-4,-4][i]; + s+=`${['Mₐ','Mₙ','Mᶜ'][i]}`; + }); + // centroid G + s+=``; + s+=`G`; + // vertices + const vNames=['A','B','C'], vPts=[A,B,C]; + vPts.forEach((V,i)=>{ + s+=``; + s+=``; + const ox2=[0,-14,14][i], oy2=[-14,14,14][i]; + s+=`${vNames[i]}`; + }); + s+=``; + + const wrap=document.getElementById('p12-med-svg'); wrap.innerHTML=s; + const svgEl=wrap.querySelector('svg'); + svgEl.querySelectorAll('.p12-vdrag').forEach(el=>{ + el.addEventListener('pointerdown',ev=>{ + if(ev.button!==undefined&&ev.button!==0) return; + const vname=el.dataset.v; + try{el.setPointerCapture(ev.pointerId);}catch(e){} + function onMove(e){ + const rect=svgEl.getBoundingClientRect(); + const sx=W/rect.width, sy=H/rect.height; + const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*sx)); + const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*sy)); + if(vname==='A') A={x:nx,y:ny}; + else if(vname==='B') B={x:nx,y:ny}; + else C={x:nx,y:ny}; + redraw(); + } + function onUp(){ el.removeEventListener('pointermove',onMove); el.removeEventListener('pointerup',onUp); el.removeEventListener('pointercancel',onUp); } + el.addEventListener('pointermove',onMove); el.addEventListener('pointerup',onUp); el.addEventListener('pointercancel',onUp); + }); + }); + + document.getElementById('p12-med-info').innerHTML=` +
|AG| : |GM_A|
${dAG.toFixed(1)} : ${dGMa.toFixed(1)} ≈ ${ratio}
+
Медиана AM_A
${dist(A,Ma).toFixed(1)}
+
Медиана BM_B
${dist(B,Mb).toFixed(1)}
+
Отношение 2:1
всегда!
`; + } + redraw(); + })(); + + /* == Доказательство == */ + (function(){ + const A={x:160,y:25}, B={x:30,y:215}, C={x:290,y:215}; + function mid(P,Q){ return {x:(P.x+Q.x)/2, y:(P.y+Q.y)/2}; } + const Ma=mid(B,C), Mb=mid(A,C); + const G={x:(A.x+B.x+C.x)/3, y:(A.y+B.y+C.y)/3}; + const steps=[ + {text:'Дано: $\\triangle ABC$, $M_a$ — середина $BC$, $M_b$ — середина $AC$, $G$ — точка пересечения медиан $AM_a$ и $BM_b$. Доказать: $AG:GM_a=2:1$.', h:'base'}, + {text:'Шаг 1. Рассмотрим среднюю линию $M_aM_b$ треугольника $ABC$. По свойству средней линии: $M_aM_b \\parallel AB$ и $M_aM_b = \\dfrac{1}{2}AB$.', h:'midline'}, + {text:'Шаг 2. Рассмотрим $\\triangle AM_bG$ и $\\triangle M_aM_bG$. $\\angle GAM_b = \\angle GM_aM_b$ (как накрест лежащие при $AB\\parallel M_aM_b$); $\\angle AGM_b = \\angle M_aGM_b$ (вертикальные).', h:'simtri'}, + {text:'Шаг 3. $\\triangle AGM_b \\sim \\triangle M_aGM_b$ по двум углам. Коэффициент подобия: $k = AB/(M_aM_b) = 2$.', h:'simtri'}, + {text:'Шаг 4. Значит $AG/GM_a = 2/1$. Аналогично для остальных медиан. Все три медианы делятся точкой $G$ в отношении $2:1$. $\\square$', h:'done'}, + ]; + let step=0; + function draw(h){ + let s=``; + s+=``; + if(h==='midline'||h==='simtri'||h==='done'){ + s+=``; + } + if(h==='simtri'||h==='done'){ + s+=``; + s+=``; + } + // medians + s+=``; + s+=``; + s+=``; + s+=`G`; + [[A,'A'],[B,'B'],[C,'C'],[Ma,'Mₐ'],[Mb,'Mₙ']].forEach(([P,lbl])=>{ + s+=``; + s+=`${lbl}`; + }); + s+=``; + document.getElementById('p12-proof-svg').innerHTML=s; + } + function show(){ const st=steps[step]; document.getElementById('p12-proof-step').innerHTML=st.text; renderMath(document.getElementById('p12-proof-step')); draw(st.h); document.getElementById('p12-proof-next').textContent=step{ if(step{step=0;show();document.getElementById('p12-proof-next').disabled=false;}); + show(); + })(); + + /* == Калькулятор == */ + (function(){ + function calc(){ + const m=+document.getElementById('p12-cmed').value; + if(m<=0||isNaN(m)){ document.getElementById('p12-calc-out').innerHTML='Введи положительное число.'; return; } + const AG=m*2/3, GM=m/3; + document.getElementById('p12-calc-out').innerHTML=`$AM = ${m}$ (вся медиана)
$AG = \\dfrac{2}{3} \\cdot ${m} = ${fmt(AG)}$ (от вершины до центроида)
$GM_A = \\dfrac{1}{3} \\cdot ${m} = ${fmt(GM)}$ (от центроида до середины стороны)
Отношение $AG:GM_A = ${fmt(AG)}:${fmt(GM)} = 2:1$ ✓`; + renderMath(document.getElementById('p12-calc-out')); + addXp(2,'p12-calc'); + } + document.getElementById('p12-calc-go').addEventListener('click',calc); + calc(); + })(); + + /* == Тренажёр == */ + (function(){ + const tasks=[ + {q:'Медиана $AM=18$. Найди $|AG|$.', ans:12, hint:'AG=(2/3)·18=12'}, + {q:'Медиана $BM=15$. Найди $|GM|$.', ans:5, hint:'GM=(1/3)·15=5'}, + {q:'$|GM_A|=4$. Найди длину медианы $AM_A$.', ans:12, hint:'AM=3·GM=3·4=12'}, + {q:'$|AG|=10$. Найди $|GM_A|$.', ans:5, hint:'GM=AG/2=10/2=5'}, + {q:'Медиана $CM=21$. Найди $|CG|$.', ans:14, hint:'CG=(2/3)·21=14'}, + ]; + let idx=0,score=0; + function show(){ document.getElementById('p12-tr-i').textContent=idx+1; document.getElementById('p12-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p12-tr-task')); document.getElementById('p12-tr-ans').value=''; document.getElementById('p12-tr-fb').style.display='none'; } + document.getElementById('p12-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p12-tr-score').textContent=0;show();}); + document.getElementById('p12-tr-go').addEventListener('click',()=>{ + const ans=+document.getElementById('p12-tr-ans').value; const fb=document.getElementById('p12-tr-fb'); + if(ans===tasks[idx].ans){ score++;document.getElementById('p12-tr-score').textContent=score;addXp(3,'p12-train');bumpProgress('p12',5); if(idxshow(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p12-train-all');} } + else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint); + }); + document.getElementById('p12-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p12-tr-go').click();}); + show(); + })(); + + /* == Босс §12 == */ + (function(){ + const tasks=[ + {q:'$|GM_A|=6$. Найди $|AG|$.', ans:12, hint:'AG=2·GM=2·6=12'}, + {q:'Медиана $AM=27$. Найди $|AG|$.', ans:18, hint:'AG=(2/3)·27=18'}, + {q:'$|AG|=8$. Найди длину медианы $AM$.', ans:12, hint:'AM=(3/2)·AG=(3/2)·8=12'}, + {q:'Три медианы имеют длины $12$, $15$, $18$. Найди $|GM_A|$ для медианы длиной $12$.', ans:4, hint:'GM=(1/3)·12=4'}, + ]; + const bossBox=document.getElementById('p12-boss-tasks'); + bossBox.innerHTML=tasks.map((t,i)=>` +
+
${t.q}
+
+ + +
+ +
`).join(''); + window.p12BossSolved=new Set(); + })(); + renderMath(box); +} +function buildP13(){ + const box = document.getElementById('p13-body'); + let html = ''; + + html += makeCard('theory','Средняя линия треугольника','13.1',` +

Средняя линия треугольника — отрезок, соединяющий середины двух его сторон.

+

В $\\triangle ABC$: $M_1$ — середина $AB$, $M_2$ — середина $AC$. Тогда $M_1M_2$ — средняя линия, параллельная $BC$.

`); + + html += makeCard('rule','Свойство средней линии','13.2',` +

Теорема. Средняя линия треугольника параллельна третьей стороне и равна её половине:

+ \\[M_1M_2 \\parallel BC, \\quad M_1M_2 = \\frac{1}{2}BC\\] +

В треугольнике три средние линии — они образуют срединный треугольник, делящий исходный на 4 равных треугольника.

`); + + html += makeCard('example','Периметр срединного треугольника','13.3',` +

Если стороны $\\triangle ABC$ равны $a$, $b$, $c$, то стороны срединного треугольника:

+ \\[\\frac{a}{2},\\quad \\frac{b}{2},\\quad \\frac{c}{2}\\] +

Периметр срединного треугольника $= \\dfrac{a+b+c}{2} = \\dfrac{P}{2}$.

`); + + /* INTERACTIVE 1: SVG треугольник со средними линиями */ + html += `
+
ИНТЕРАКТИВ 1
Живые средние линии — тащи вершины
+
Перетащи A, B, C. Все три средние линии показаны, срединный треугольник подсвечен.
+
+
+
`; + + /* INTERACTIVE 2: Доказательство */ + html += `
+
ИНТЕРАКТИВ 2
Доказательство свойства средней линии
+
+
+
+ + +
+
`; + + /* INTERACTIVE 3: Тренажёр */ + html += `
+
ИНТЕРАКТИВ 3
Тренажёр задач на среднюю линию треугольника
+
Задача 1 / 5Очки: 0
+
+
+ + + +
+ +
`; + + /* INTERACTIVE 4: Mini-quiz верно/неверно */ + html += `
+
ИНТЕРАКТИВ 4
Верно или неверно?
+
+
+ +
`; + + /* INTERACTIVE 5: DnD */ + html += `
+
ИНТЕРАКТИВ 5
Распознай среднюю линию
+
Перетащи отрезки: какие являются средними линиями треугольника?
+ ${DND_HINT_HTML} +
+
+
Средняя линия
+
Не средняя линия
+
+
+ +
`; + + /* INTERACTIVE 6: Босс §13 */ + html += `
+
БОСС §13
Итоговые задачи
+
+5 XP за каждую верную задачу.
+
+
`; + + html += `
+ +
`; + + html += secNav('p12','p14'); + box.innerHTML = html; + + /* == SVG средние линии == */ + (function(){ + const W=380, H=300; + let A={x:190,y:30}, B={x:40,y:270}, C={x:340,y:270}; + function mid(P,Q){ return {x:(P.x+Q.x)/2, y:(P.y+Q.y)/2}; } + function dist(P,Q){ return Math.hypot(Q.x-P.x, Q.y-P.y); } + + function redraw(){ + const M1=mid(A,B), M2=mid(A,C), M3=mid(B,C); + let s=``; + // outer triangle + s+=``; + // median triangle fill + s+=``; + // middle lines labels + const mlCols=['#10b981','#f59e0b','#8b5cf6']; + const mlPairs=[[M1,M2],[M2,M3],[M1,M3]]; + const mlLabels=['M₁M₂ ∥ BC','M₂M₃ ∥ AB','M₁M₃ ∥ AC']; + mlPairs.forEach(([P,Q],i)=>{ + const mx=(P.x+Q.x)/2, my=(P.y+Q.y)/2; + s+=`${mlLabels[i]}`; + }); + // midpoints + [[M1,'M₁'],[M2,'M₂'],[M3,'M₃']].forEach(([P,lbl],i)=>{ + s+=``; + s+=`${lbl}`; + }); + // vertices + ['A','B','C'].forEach((lbl,i)=>{ + const V=[A,B,C][i]; + s+=``; + s+=``; + const ox=[0,-16,16][i], oy=[-16,14,14][i]; + s+=`${lbl}`; + }); + s+=``; + const wrap=document.getElementById('p13-ml-svg'); wrap.innerHTML=s; + const svgEl=wrap.querySelector('svg'); + svgEl.querySelectorAll('.p13-vd').forEach(el=>{ + el.addEventListener('pointerdown',ev=>{ + if(ev.button!==undefined&&ev.button!==0) return; + const vname=el.dataset.v; + try{el.setPointerCapture(ev.pointerId);}catch(e){} + function onMove(e){ + const rect=svgEl.getBoundingClientRect(); + const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*W/rect.width)); + const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*H/rect.height)); + if(vname==='A') A={x:nx,y:ny}; + else if(vname==='B') B={x:nx,y:ny}; + else C={x:nx,y:ny}; + redraw(); + } + function onUp(){ el.removeEventListener('pointermove',onMove); el.removeEventListener('pointerup',onUp); el.removeEventListener('pointercancel',onUp); } + el.addEventListener('pointermove',onMove); el.addEventListener('pointerup',onUp); el.addEventListener('pointercancel',onUp); + }); + }); + const bc=dist(B,C), ac=dist(A,C), ab=dist(A,B); + const m12=dist(M1,M2), m23=dist(M2,M3), m13=dist(M1,M3); + document.getElementById('p13-ml-info').innerHTML=` +
M₁M₂ = BC/2
${m12.toFixed(1)} = ${(bc/2).toFixed(1)}
+
M₂M₃ = AB/2
${m23.toFixed(1)} = ${(ab/2).toFixed(1)}
+
M₁M₃ = AC/2
${m13.toFixed(1)} = ${(ac/2).toFixed(1)}
+
P срединного / P исх.
${(m12+m23+m13).toFixed(1)} / ${(bc+ac+ab).toFixed(1)} = 1:2
`; + } + redraw(); + })(); + + /* == Доказательство == */ + (function(){ + const A={x:160,y:25}, B={x:30,y:215}, C={x:290,y:215}; + function mid(P,Q){ return {x:(P.x+Q.x)/2, y:(P.y+Q.y)/2}; } + const M1=mid(A,B), M2=mid(A,C); + const steps=[ + {text:'Дано: $\\triangle ABC$, $M_1$ — середина $AB$, $M_2$ — середина $AC$. Доказать: $M_1M_2 \\parallel BC$, $M_1M_2 = \\dfrac{1}{2}BC$.', h:'base'}, + {text:'Шаг 1. Отложим от $M_2$ отрезок $M_2D = M_1M_2$, так что $D$ лежит на луче $M_1M_2$ за $M_2$.', h:'step1'}, + {text:'Шаг 2. Рассмотрим $\\triangle AM_1M_2$ и $\\triangle CM_2D$. $AM_1=CM_2=\\dfrac{1}{2}$-сторон; $\\angle AM_1M_2=\\angle CDM_2$ (вертикальные); $M_1M_2=M_2D$. По признаку «два угла и сторона»: $\\triangle AM_1M_2 \\cong \\triangle CDM_2$.', h:'step2'}, + {text:'Шаг 3. Из равенства треугольников: $AM_1=DC$ и $\\angle M_1AM_2=\\angle DCM_2$, значит $AM_1 \\parallel DC$, т.е. $AB \\parallel CD$.', h:'step3'}, + {text:'Вывод. $M_1BDC$ — параллелограмм ($M_1B \\parallel CD$, $M_1B=DC$). Значит $M_1D \\parallel BC$ и $BD=M_1M_2$. Но $BD=\\dfrac{1}{2}BC$ (так как $M_1$ — середина). Итак, $M_1M_2 \\parallel BC$ и $M_1M_2=\\dfrac{1}{2}BC$. $\\square$', h:'done'}, + ]; + let step=0; + function draw(h){ + const D={x:M2.x+(M2.x-M1.x), y:M2.y+(M2.y-M1.y)}; + let s=``; + s+=``; + s+=``; + if(h==='step1'||h==='step2'||h==='step3'||h==='done'){ + s+=``; + s+=``; + s+=`D`; + } + if(h==='step2'||h==='step3'||h==='done'){ + s+=``; + s+=``; + } + if(h==='done'){ + s+=``; + } + [[A,'A'],[B,'B'],[C,'C'],[M1,'M₁'],[M2,'M₂']].forEach(([P,lbl])=>{ + s+=``; + s+=`${lbl}`; + }); + s+=``; + document.getElementById('p13-proof-svg').innerHTML=s; + } + function show(){ const st=steps[step]; document.getElementById('p13-proof-step').innerHTML=st.text; renderMath(document.getElementById('p13-proof-step')); draw(st.h); document.getElementById('p13-proof-next').textContent=step{ if(step{step=0;show();document.getElementById('p13-proof-next').disabled=false;}); + show(); + })(); + + /* == Тренажёр == */ + (function(){ + const tasks=[ + {q:'$BC=14$. Найди среднюю линию, параллельную $BC$.', ans:7, hint:'M₁M₂=BC/2=14/2=7'}, + {q:'Средняя линия $= 9$. Найди сторону, которой она параллельна.', ans:18, hint:'BC=2·M₁M₂=2·9=18'}, + {q:'Стороны треугольника $12, 16, 20$. Найди периметр срединного треугольника.', ans:24, hint:'P/2=(12+16+20)/2=48/2=24'}, + {q:'Средняя линия треугольника $= 11$. Найди вторую среднюю линию, параллельную стороне $AB=16$.', ans:8, hint:'M₂M₃=AB/2=16/2=8'}, + {q:'$BC=30$, средняя линия $M_1M_2$. Найди $M_1M_2$.', ans:15, hint:'M₁M₂=BC/2=30/2=15'}, + ]; + let idx=0,score=0; + function show(){ document.getElementById('p13-tr-i').textContent=idx+1; document.getElementById('p13-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p13-tr-task')); document.getElementById('p13-tr-ans').value=''; document.getElementById('p13-tr-fb').style.display='none'; } + document.getElementById('p13-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p13-tr-score').textContent=0;show();}); + document.getElementById('p13-tr-go').addEventListener('click',()=>{ + const ans=+document.getElementById('p13-tr-ans').value; const fb=document.getElementById('p13-tr-fb'); + if(ans===tasks[idx].ans){ score++;document.getElementById('p13-tr-score').textContent=score;addXp(3,'p13-train');bumpProgress('p13',5); if(idxshow(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p13-train-all');} } + else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint); + }); + document.getElementById('p13-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p13-tr-go').click();}); + show(); + })(); + + /* == Mini-quiz == */ + (function(){ + const qs=[ + {text:'Средняя линия треугольника параллельна третьей стороне.', ans:true}, + {text:'Средняя линия треугольника равна третьей стороне.', ans:false}, + {text:'В треугольнике три средние линии.', ans:true}, + {text:'Срединный треугольник делит исходный на 4 равных треугольника.', ans:true}, + ]; + const list=document.getElementById('p13-quiz-list'); + list.innerHTML=qs.map((q,i)=>` +
+
${q.text}
+
+ + +
+
`).join(''); + const sel={}; + list.querySelectorAll('.p13-qbtn').forEach(btn=>{ + btn.addEventListener('click',()=>{ + const i=btn.dataset.i; + sel[i]=btn.dataset.v==='true'; + list.querySelectorAll(`.p13-qbtn[data-i="${i}"]`).forEach(b=>b.className='btn p13-qbtn'); + btn.className='btn primary p13-qbtn'; + }); + }); + document.getElementById('p13-quiz-check').addEventListener('click',()=>{ + let ok=0; + qs.forEach((q,i)=>{ if(sel[i]===q.ans) ok++; }); + const fb=document.getElementById('p13-quiz-fb'); + if(ok===qs.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p13-quiz');bumpProgress('p13',15);} + else feedback(fb,false,'Верно: '+ok+' из '+qs.length+'. Средняя линия = ПОЛОВИНА стороны (не равна ей).'); + }); + document.getElementById('p13-quiz-reset').addEventListener('click',()=>{ Object.keys(sel).forEach(k=>delete sel[k]); list.querySelectorAll('.p13-qbtn').forEach(b=>b.className='btn p13-qbtn'); document.getElementById('p13-quiz-fb').style.display='none'; }); + })(); + + /* == DnD == */ + (function(){ + const items=[ + {id:'s1',html:'Отрезок, соединяющий середины AB и AC', ans:'yes'}, + {id:'s2',html:'Медиана из A к середине BC', ans:'no'}, + {id:'s3',html:'Отрезок, соединяющий середины BC и AC', ans:'yes'}, + {id:'s4',html:'Высота из A на BC', ans:'no'}, + {id:'s5',html:'Отрезок, соединяющий середины AB и BC', ans:'yes'}, + {id:'s6',html:'Биссектриса угла A', ans:'no'}, + ]; + const sorter=setupSorter({poolId:'p13-dnd-pool',scopeSelector:'#p13-dnd-wrap',items,cats:['yes','no']}); + document.getElementById('p13-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p13-dnd-fb').style.display='none';}); + document.getElementById('p13-dnd-check').addEventListener('click',()=>{ + let ok=0; items.forEach(it=>{if(sorter.placed[it.id]===it.ans)ok++;}); + const fb=document.getElementById('p13-dnd-fb'); + if(ok===items.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p13-dnd');bumpProgress('p13',15);} + else feedback(fb,false,'Верно: '+ok+' из '+items.length+'. Средняя линия соединяет середины двух сторон.'); + }); + })(); + + /* == Босс §13 == */ + (function(){ + const tasks=[ + {q:'$BC=26$. Найди среднюю линию, параллельную $BC$.', ans:13, hint:'M₁M₂=BC/2=26/2=13'}, + {q:'Средняя линия $= 8.5$. Найди параллельную ей сторону.', ans:17, hint:'BC=2·8.5=17'}, + {q:'Стороны треугольника $10, 24, 26$. Найди периметр срединного треугольника.', ans:30, hint:'P_mid=(10+24+26)/2=60/2=30'}, + {q:'$M_1M_2 = 7$, $M_2M_3 = 9$, $M_1M_3 = 5$. Найди наибольшую сторону треугольника.', ans:18, hint:'BC=2·M₁M₂=2·9=18'}, + ]; + const bossBox=document.getElementById('p13-boss-tasks'); + bossBox.innerHTML=tasks.map((t,i)=>` +
+
${t.q}
+
+ + +
+ +
`).join(''); + window.p13BossSolved=new Set(); + })(); + renderMath(box); +} +function buildP14(){ + const box = document.getElementById('p14-body'); + let html = ''; + + html += makeCard('theory','Трапеция — определение','14.1',` +

Трапеция — четырёхугольник, у которого только одна пара противоположных сторон параллельна.

+

Параллельные стороны называются основаниями (обычно $a$ — большее, $b$ — меньшее), а две другие — боковыми сторонами.

+

Виды трапеций:

+
    +
  • Произвольная — боковые стороны не равны.
  • +
  • Равнобедренная — боковые стороны равны: $AD=BC$.
  • +
  • Прямоугольная — один из углов при боковой стороне прямой.
  • +
`); + + html += makeCard('rule','Средняя линия трапеции','14.2',` +

Средняя линия трапеции — отрезок $MN$, соединяющий середины боковых сторон.

+

Свойство: средняя линия параллельна основаниям и равна их полусумме:

+ \\[MN \\parallel AD \\parallel BC, \\quad MN = \\frac{a+b}{2}\\] +

Площадь трапеции: $S = \\dfrac{(a+b)}{2} \\cdot h = MN \\cdot h$, где $h$ — высота.

`); + + html += makeCard('example','Формулы для трапеции','14.3',` +

Дано: основания $a$ и $b$, высота $h$, средняя линия $m$.

+ \\[m = \\frac{a+b}{2} \\quad \\Rightarrow \\quad a+b = 2m\\] + \\[S = m \\cdot h = \\frac{(a+b)}{2} \\cdot h\\] +

Из $m$ найти неизвестное основание: $b = 2m - a$.

`); + + /* INTERACTIVE 1: SVG-трапеция draggable */ + html += `
+
ИНТЕРАКТИВ 1
Живая трапеция — тащи вершины
+
Перетащи вершины A, B, C, D. Средняя линия и все формулы пересчитываются живо.
+
+
+
`; + + /* INTERACTIVE 2: Конструктор типов */ + html += `
+
ИНТЕРАКТИВ 2
Конструктор типов трапеций
+
Переключай тип — трапеция меняет форму, свойства обновляются.
+
+ + + +
+
+
+
`; + + /* INTERACTIVE 3: Доказательство */ + html += `
+
ИНТЕРАКТИВ 3
Доказательство свойства средней линии трапеции
+
+
+
+ + +
+
`; + + /* INTERACTIVE 4: Калькулятор */ + html += `
+
ИНТЕРАКТИВ 4
Калькулятор трапеции
+
Введи два из трёх значений (a, b, m) — найди третье. Добавь высоту для площади.
+
+ + + + +
+
+
`; + + /* INTERACTIVE 5: Тренажёр */ + html += `
+
ИНТЕРАКТИВ 5
Тренажёр задач на трапецию
+
Задача 1 / 5Очки: 0
+
+
+ + + +
+ +
`; + + /* INTERACTIVE 6: Босс §14 */ + html += `
+
БОСС §14
Итоговые задачи
+
+5 XP за каждую верную задачу.
+
+
`; + + html += `
+ +
`; + + html += secNav('p13','p15'); + box.innerHTML = html; + + /* == SVG трапеция draggable == */ + (function(){ + const W=400, H=280; + let A={x:60,y:220}, B={x:340,y:220}, C={x:280,y:70}, D={x:120,y:70}; + // Keep BC parallel to AD: when dragging C or D, we sync y-coordinate + function dist(P,Q){ return Math.hypot(Q.x-P.x, Q.y-P.y); } + + function redraw(){ + const Ma={x:(A.x+D.x)/2, y:(A.y+D.y)/2}; + const Mb={x:(B.x+C.x)/2, y:(B.y+C.y)/2}; + const a=dist(A,B), b=dist(D,C); + const m=(a+b)/2; + const h=Math.abs(A.y-D.y); + + let s=``; + s+=``; + // height dashes + s+=``; + // median line + s+=``; + s+=``; + s+=``; + const mLx=((Ma.x+Mb.x)/2).toFixed(1), mLy=(((Ma.y+Mb.y)/2)-10).toFixed(1); + s+=`m=${m.toFixed(1)}`; + // base labels + s+=`a=${a.toFixed(1)}`; + s+=`b=${b.toFixed(1)}`; + // height label + s+=`h=${h.toFixed(0)}`; + // vertices draggable + const vNames=['A','B','C','D']; + [A,B,C,D].forEach((V,i)=>{ + s+=``; + s+=``; + const offx=[-14,12,12,-14][i], offy=[12,12,-12,-12][i]; + s+=`${vNames[i]}`; + }); + s+=``; + const wrap=document.getElementById('p14-trap-svg'); wrap.innerHTML=s; + const svgEl=wrap.querySelector('svg'); + svgEl.querySelectorAll('.p14-vd').forEach(el=>{ + el.addEventListener('pointerdown',ev=>{ + if(ev.button!==undefined&&ev.button!==0) return; + const vname=el.dataset.v; + try{el.setPointerCapture(ev.pointerId);}catch(e){} + function onMove(e){ + const rect=svgEl.getBoundingClientRect(); + const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*W/rect.width)); + const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*H/rect.height)); + if(vname==='A'){ A={x:nx,y:ny}; B.y=ny; } + else if(vname==='B'){ B={x:nx,y:ny}; A.y=ny; } + else if(vname==='C'){ C={x:nx,y:ny}; D.y=ny; } + else if(vname==='D'){ D={x:nx,y:ny}; C.y=ny; } + redraw(); + } + function onUp(){ el.removeEventListener('pointermove',onMove); el.removeEventListener('pointerup',onUp); el.removeEventListener('pointercancel',onUp); } + el.addEventListener('pointermove',onMove); el.addEventListener('pointerup',onUp); el.addEventListener('pointercancel',onUp); + }); + }); + const S=(a+b)/2*h; + document.getElementById('p14-trap-info').innerHTML=` +
Основание a
${a.toFixed(1)}
+
Основание b
${b.toFixed(1)}
+
Средняя линия m=(a+b)/2
${m.toFixed(1)}
+
Площадь S=m·h
${S.toFixed(1)}
`; + } + redraw(); + })(); + + /* == Конструктор типов == */ + (function(){ + function drawType(type){ + const W2=340, H2=200; + let pts, info; + if(type==='arb'){ + pts=[[50,170],[290,170],[230,50],[110,50]]; + info='Произвольная трапеция: $AD \\parallel BC$, боковые стороны не равны.'; + } else if(type==='iso'){ + pts=[[50,170],[290,170],[220,50],[120,50]]; + info='Равнобедренная трапеция: $AD \\parallel BC$, $AB = CD$ (боковые стороны равны). Углы при каждом основании равны.'; + } else { + pts=[[50,170],[290,170],[290,50],[50,50]]; + info='Прямоугольная трапеция: $AD \\parallel BC$, один угол $90°$ при боковой стороне $AB$.'; + } + const [A2,B2,C2,D2]=pts.map(p=>({x:p[0],y:p[1]})); + const Ma2={x:(A2.x+D2.x)/2, y:(A2.y+D2.y)/2}; + const Mb2={x:(B2.x+C2.x)/2, y:(B2.y+C2.y)/2}; + let s=``; + s+=``; + s+=``; + if(type==='right'){ + const sq=8; + s+=``; + } + if(type==='iso'){ + // equal side marks + function tickMark(P,Q,col){ + const mx=(P.x+Q.x)/2, my=(P.y+Q.y)/2; + const dx=Q.x-P.x, dy=Q.y-P.y, l=Math.hypot(dx,dy); + const nx=-dy/l*6, ny=dx/l*6; + return ``; + } + s+=tickMark(A2,D2,'#10b981'); s+=tickMark(B2,C2,'#10b981'); + } + ['A','B','C','D'].forEach((lbl,i)=>{ + const V=pts[i]; + const offx=[-16,12,12,-16][i], offy=[12,12,-12,-12][i]; + s+=`${lbl}`; + }); + s+=``; + document.getElementById('p14-type-svg').innerHTML=s; + document.getElementById('p14-type-info').innerHTML=info; + renderMath(document.getElementById('p14-type-info')); + } + document.getElementById('p14-type-arb').addEventListener('click',()=>{ ['arb','iso','right'].forEach(t=>document.getElementById('p14-type-'+t).className='btn'); document.getElementById('p14-type-arb').className='btn primary'; drawType('arb'); }); + document.getElementById('p14-type-iso').addEventListener('click',()=>{ ['arb','iso','right'].forEach(t=>document.getElementById('p14-type-'+t).className='btn'); document.getElementById('p14-type-iso').className='btn primary'; drawType('iso'); }); + document.getElementById('p14-type-right').addEventListener('click',()=>{ ['arb','iso','right'].forEach(t=>document.getElementById('p14-type-'+t).className='btn'); document.getElementById('p14-type-right').className='btn primary'; drawType('right'); }); + drawType('arb'); + })(); + + /* == Доказательство == */ + (function(){ + const A3={x:50,y:190}, B3={x:290,y:190}, C3={x:230,y:60}, D3={x:110,y:60}; + const M={x:(A3.x+D3.x)/2, y:(A3.y+D3.y)/2}; + const N={x:(B3.x+C3.x)/2, y:(B3.y+C3.y)/2}; + const steps=[ + {text:'Дано: трапеция $ABCD$, $AD \\parallel BC$. $M$ — середина $AB$, $N$ — середина $CD$. Доказать: $MN \\parallel AD$, $MN = \\dfrac{AD+BC}{2}$.', h:'base'}, + {text:'Шаг 1. Проведём диагональ $AC$. Рассмотрим $\\triangle ABC$: $M$ — середина $AB$, пересечение $MN$ с $AC$ — середина $AC$ (назовём $P$).', h:'diag'}, + {text:'Шаг 2. В $\\triangle ABC$: $MP$ — средняя линия, $MP \\parallel BC$, $MP = \\dfrac{BC}{2}$.', h:'midABC'}, + {text:'Шаг 3. В $\\triangle ACD$: $P$ — середина $AC$, $N$ — середина $CD$. $PN$ — средняя линия, $PN \\parallel AD$, $PN = \\dfrac{AD}{2}$.', h:'midACD'}, + {text:'Вывод. $MP \\parallel BC \\parallel AD$, $PN \\parallel AD \\Rightarrow M$, $P$, $N$ коллинеарны и $MN \\parallel AD$.
$MN = MP + PN = \\dfrac{BC}{2} + \\dfrac{AD}{2} = \\dfrac{AD+BC}{2}$. $\\square$', h:'done'}, + ]; + let step=0; + function draw(h){ + const P={x:(A3.x+C3.x)/2, y:(A3.y+C3.y)/2}; + let s=``; + s+=``; + s+=``; + if(h==='diag'||h==='midABC'||h==='midACD'||h==='done'){ + s+=``; + s+=``; + s+=`P`; + } + if(h==='midABC'||h==='done'){ + s+=``; + } + if(h==='midACD'||h==='done'){ + s+=``; + } + [[A3,'A'],[B3,'B'],[C3,'C'],[D3,'D'],[M,'M'],[N,'N']].forEach(([P2,lbl])=>{ + s+=``; + s+=`${lbl}`; + }); + s+=``; + document.getElementById('p14-proof-svg').innerHTML=s; + } + function show(){ const st=steps[step]; document.getElementById('p14-proof-step').innerHTML=st.text; renderMath(document.getElementById('p14-proof-step')); draw(st.h); document.getElementById('p14-proof-next').textContent=step{ if(step{step=0;show();document.getElementById('p14-proof-next').disabled=false;}); + show(); + })(); + + /* == Калькулятор == */ + (function(){ + function calc(){ + const a=+document.getElementById('p14-ca').value; + const b=+document.getElementById('p14-cb').value; + const h=+document.getElementById('p14-ch').value; + if(a<=0||b<=0||isNaN(a)||isNaN(b)){ document.getElementById('p14-calc-out').innerHTML='Введи положительные основания.'; return; } + const m=(a+b)/2; + const S=h>0?m*h:null; + let html2=`$m = \\dfrac{a+b}{2} = \\dfrac{${a}+${b}}{2} = ${m}$`; + if(S!==null) html2+=`
$S = m \\cdot h = ${m} \\cdot ${h} = ${S}$`; + html2+=`
Если известно $m=${m}$ и $a=${a}$, то $b = 2m - a = ${2*m} - ${a} = ${b}$`; + document.getElementById('p14-calc-out').innerHTML=html2; + renderMath(document.getElementById('p14-calc-out')); + addXp(2,'p14-calc'); + } + document.getElementById('p14-calc-go').addEventListener('click',calc); + calc(); + })(); + + /* == Тренажёр == */ + (function(){ + const tasks=[ + {q:'Основания трапеции $a=10$, $b=6$. Найди среднюю линию.', ans:8, hint:'m=(10+6)/2=8'}, + {q:'Средняя линия $m=9$, основание $a=14$. Найди второе основание $b$.', ans:4, hint:'b=2m-a=18-14=4'}, + {q:'Основания $a=15$, $b=7$, высота $h=4$. Найди площадь.', ans:44, hint:'S=(15+7)/2·4=11·4=44'}, + {q:'Средняя линия $m=11$, высота $h=6$. Найди площадь.', ans:66, hint:'S=m·h=11·6=66'}, + {q:'Основания $a=20$, $b=12$. Найди среднюю линию.', ans:16, hint:'m=(20+12)/2=16'}, + ]; + let idx=0,score=0; + function show(){ document.getElementById('p14-tr-i').textContent=idx+1; document.getElementById('p14-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p14-tr-task')); document.getElementById('p14-tr-ans').value=''; document.getElementById('p14-tr-fb').style.display='none'; } + document.getElementById('p14-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p14-tr-score').textContent=0;show();}); + document.getElementById('p14-tr-go').addEventListener('click',()=>{ + const ans=+document.getElementById('p14-tr-ans').value; const fb=document.getElementById('p14-tr-fb'); + if(ans===tasks[idx].ans){ score++;document.getElementById('p14-tr-score').textContent=score;addXp(3,'p14-train');bumpProgress('p14',5); if(idxshow(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p14-train-all');} } + else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint); + }); + document.getElementById('p14-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p14-tr-go').click();}); + show(); + })(); + + /* == Босс §14 == */ + (function(){ + const tasks=[ + {q:'Основания трапеции $a=18$, $b=10$. Найди среднюю линию.', ans:14, hint:'m=(18+10)/2=14'}, + {q:'Средняя линия $m=13$, основание $b=9$. Найди $a$.', ans:17, hint:'a=2m-b=26-9=17'}, + {q:'Средняя линия $m=7$, высота $h=8$. Найди площадь.', ans:56, hint:'S=7·8=56'}, + {q:'Основания $a=24$, $b=16$, высота $h=5$. Найди площадь.', ans:100, hint:'S=(24+16)/2·5=20·5=100'}, + ]; + const bossBox=document.getElementById('p14-boss-tasks'); + bossBox.innerHTML=tasks.map((t,i)=>` +
+
${t.q}
+
+ + +
+ +
`).join(''); + window.p14BossSolved=new Set(); + })(); + renderMath(box); +} +function buildP15(){ + const box = document.getElementById('p15-body'); + let html = ''; + + html += makeCard('theory','Равнобедренная трапеция','15.1',` +

Равнобедренная трапеция — трапеция, у которой боковые стороны равны: $AB = CD$.

+

Свойство 1. Углы при каждом основании равны: $\\angle A = \\angle B$, $\\angle C = \\angle D$.

+

Свойство 2. Диагонали равны: $AC = BD$.

+

Свойство 3. Сумма углов при одной боковой стороне равна $180°$: $\\angle A + \\angle D = 180°$, $\\angle B + \\angle C = 180°$.

`); + + html += makeCard('rule','Доказательство свойства 1: углы при основании','15.2',` +

Из $A$ и $B$ опустим высоты $AH_1$ и $BH_2$ на $CD$ (нижнее основание).

+

$\\triangle AH_1D$ и $\\triangle BH_2C$: $AH_1=BH_2$ (высоты в трапеции с равными боковыми), $AD=BC$ (условие), $\\angle H_1=\\angle H_2=90°$.

+

По «гипотенуза-катет»: $\\triangle AH_1D \\cong \\triangle BH_2C \\Rightarrow \\angle D = \\angle C$.

+

Аналогично $\\angle A = \\angle B$. $\\square$

`); + + html += makeCard('rule','Доказательство свойства 2: диагонали равны','15.3',` +

Рассмотрим $\\triangle ABD$ и $\\triangle BAC$ (общее основание $AB$).

+

$AD = BC$ (равнобедренная), $\\angle A = \\angle B$ (свойство 1), $AB = AB$.

+

По признаку «два угла и сторона»: $\\triangle ABD \\cong \\triangle BAC \\Rightarrow BD = AC$. $\\square$

`); + + /* INTERACTIVE 1: SVG равнобедренная трапеция */ + html += `
+
ИНТЕРАКТИВ 1
Живая равнобедренная трапеция — тащи вершины
+
Трапеция остаётся равнобедренной. Наблюдай: боковые стороны равны, диагонали равны, углы при основании равны.
+
+
+
`; + + /* INTERACTIVE 2: Доказательство 1 */ + html += `
+
ИНТЕРАКТИВ 2
Доказательство: углы при основании равны
+
+
+
+ + +
+
`; + + /* INTERACTIVE 3: Доказательство 2 */ + html += `
+
ИНТЕРАКТИВ 3
Доказательство: диагонали равны
+
+
+
+ + +
+
`; + + /* INTERACTIVE 4: Тренажёр */ + html += `
+
ИНТЕРАКТИВ 4
Тренажёр задач на равнобедренную трапецию
+
Задача 1 / 5Очки: 0
+
+
+ + + +
+ +
`; + + /* INTERACTIVE 5: DnD */ + html += `
+
ИНТЕРАКТИВ 5
Чьё это свойство?
+
Распредели свойства по фигурам.
+ ${DND_HINT_HTML} +
+
+
Любая трапеция
+
Только равнобедренная
+
Прямоугольник тоже
+
+
+ +
`; + + /* INTERACTIVE 6: Босс §15 */ + html += `
+
БОСС §15
Итоговые задачи
+
+5 XP за каждую верную задачу.
+
+
`; + + html += `
+ +
`; + + html += secNav('p14','p16'); + box.innerHTML = html; + + /* == SVG равнобедренная трапеция == */ + (function(){ + const W=400, H=280; + // Keep it isosceles: D and C are symmetric around centerX, top base fixed y + let cx=200, topY=60, botY=220, halfTop=80, halfBot=140; + + function dist(P,Q){ return Math.hypot(Q.x-P.x, Q.y-P.y); } + function angDeg(P,O,Q){ const ax=P.x-O.x,ay=P.y-O.y,bx=Q.x-O.x,by=Q.y-O.y; return Math.acos(Math.max(-1,Math.min(1,(ax*bx+ay*by)/(Math.hypot(ax,ay)*Math.hypot(bx,by)))))*180/Math.PI; } + + function redraw(){ + const A={x:cx-halfBot, y:botY}, B={x:cx+halfBot, y:botY}; + const C={x:cx+halfTop, y:topY}, D={x:cx-halfTop, y:topY}; + const side=dist(A,D); + const diag=dist(A,C); + const angA=angDeg(D,A,B); + const angD=angDeg(A,D,C); + + let s=``; + s+=``; + // diagonals + s+=``; + s+=``; + // diagonal labels + s+=`AC=${diag.toFixed(1)}`; + s+=`BD=${dist(B,D).toFixed(1)}`; + // equal side marks + function tickMark2(P,Q,col){ + const mx=(P.x+Q.x)/2, my=(P.y+Q.y)/2; + const dx=Q.x-P.x, dy=Q.y-P.y, l=Math.hypot(dx,dy); + const nx=-dy/l*7, ny=dx/l*7; + return ``; + } + s+=tickMark2(A,D,'#10b981'); s+=tickMark2(B,C,'#10b981'); + // angle arcs + function arcMark(O,P,Q,col,r){ + const a1=Math.atan2(P.y-O.y,P.x-O.x), a2=Math.atan2(Q.y-O.y,Q.x-O.x); + const x1=O.x+r*Math.cos(a1), y1=O.y+r*Math.sin(a1); + const x2=O.x+r*Math.cos(a2), y2=O.y+r*Math.sin(a2); + return ``; + } + s+=arcMark(A,D,B,'#ef4444',22); s+=arcMark(B,A,C,'#ef4444',22); + // vertices draggable + const vNames=['A','B','C','D'], vPts=[A,B,C,D]; + vPts.forEach((V,i)=>{ + s+=``; + s+=``; + const offx=[-14,12,12,-14][i], offy=[14,14,-12,-12][i]; + s+=`${vNames[i]}`; + }); + s+=``; + const wrap=document.getElementById('p15-trap-svg'); wrap.innerHTML=s; + const svgEl=wrap.querySelector('svg'); + svgEl.querySelectorAll('.p15-vd').forEach(el=>{ + el.addEventListener('pointerdown',ev=>{ + if(ev.button!==undefined&&ev.button!==0) return; + const vname=el.dataset.v; + try{el.setPointerCapture(ev.pointerId);}catch(e){} + function onMove(e){ + const rect=svgEl.getBoundingClientRect(); + const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*W/rect.width)); + const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*H/rect.height)); + if(vname==='A'){ halfBot=Math.max(30, cx-nx); botY=ny; } + else if(vname==='B'){ halfBot=Math.max(30, nx-cx); botY=ny; } + else if(vname==='C'){ halfTop=Math.max(10, nx-cx); topY=ny; } + else if(vname==='D'){ halfTop=Math.max(10, cx-nx); topY=ny; } + redraw(); + } + function onUp(){ el.removeEventListener('pointermove',onMove); el.removeEventListener('pointerup',onUp); el.removeEventListener('pointercancel',onUp); } + el.addEventListener('pointermove',onMove); el.addEventListener('pointerup',onUp); el.addEventListener('pointercancel',onUp); + }); + }); + const eq=(Math.abs(diag-dist(B,D))<0.5)?'Равны!':'не равны'; + document.getElementById('p15-trap-info').innerHTML=` +
Боковые AD = BC
${side.toFixed(1)}
+
Диагонали AC = BD
${eq}
+
Угол A = Угол B
${angA.toFixed(1)}° = ${angA.toFixed(1)}°
+
A+D = 180°
${(angA+angD).toFixed(1)}°
`; + } + redraw(); + })(); + + /* == Доказательство 1: углы == */ + (function(){ + const A={x:50,y:210}, B={x:290,y:210}, C={x:230,y:70}, D={x:110,y:70}; + const H1={x:A.x,y:D.y}, H2={x:B.x,y:C.y}; + const steps=[ + {text:'Дано: равнобедренная трапеция $ABCD$ ($AD \\parallel BC$, $AB = CD$). Доказать: $\\angle A = \\angle B$, $\\angle C = \\angle D$.', h:'base'}, + {text:'Шаг 1. Проведём высоты $AH_1$ и $BH_2$ из вершин $A$ и $B$ на $DC$ (или его продолжение).', h:'heights'}, + {text:'Шаг 2. В прямоугольных треугольниках $\\triangle AH_1D$ и $\\triangle BH_2C$: $AH_1 = BH_2$ (высоты параллельной трапеции), $AD = BC$ (боковые стороны).', h:'tri'}, + {text:'Шаг 3. По признаку «гипотенуза-катет»: $\\triangle AH_1D \\cong \\triangle BH_2C \\Rightarrow \\angle D = \\angle C$.', h:'tri'}, + {text:'Вывод. $\\angle A = 180° - \\angle D = 180° - \\angle C = \\angle B$. Углы при нижнем и верхнем основаниях равны попарно. $\\square$', h:'done'}, + ]; + let step=0; + function draw(h){ + let s=``; + s+=``; + if(h==='heights'||h==='tri'||h==='done'){ + s+=``; + s+=``; + const sq=7; + s+=``; + s+=``; + } + if(h==='tri'||h==='done'){ + s+=``; + s+=``; + } + [[A,'A'],[B,'B'],[C,'C'],[D,'D']].forEach(([P,lbl])=>{ + s+=``; + s+=`${lbl}`; + }); + s+=``; + document.getElementById('p15-proof1-svg').innerHTML=s; + } + function show(){ const st=steps[step]; document.getElementById('p15-proof1-step').innerHTML=st.text; renderMath(document.getElementById('p15-proof1-step')); draw(st.h); document.getElementById('p15-proof1-next').textContent=step{ if(step{step=0;show();document.getElementById('p15-proof1-next').disabled=false;}); + show(); + })(); + + /* == Доказательство 2: диагонали == */ + (function(){ + const A={x:50,y:210}, B={x:290,y:210}, C={x:230,y:70}, D={x:110,y:70}; + const steps=[ + {text:'Дано: равнобедренная трапеция $ABCD$, $\\angle A = \\angle B$ (доказано). Доказать: $AC = BD$.', h:'base'}, + {text:'Шаг 1. Рассмотрим $\\triangle ABD$ и $\\triangle BAC$.', h:'tri1'}, + {text:'Шаг 2. $AD = BC$ (боковые стороны равны), $\\angle DAB = \\angle CBA$ (свойство 1), $AB = AB$ (общее).', h:'tri2'}, + {text:'Шаг 3. По признаку «два угла и сторона»: $\\triangle ABD \\cong \\triangle BAC$.', h:'tri2'}, + {text:'Вывод. $BD = AC$ — диагонали равны. $\\square$', h:'done'}, + ]; + let step=0; + function draw(h){ + let s=``; + s+=``; + if(h==='tri1'||h==='tri2'||h==='done'){ + s+=``; + s+=``; + } + if(h==='tri2'||h==='done'){ + s+=``; + s+=``; + } + [[A,'A'],[B,'B'],[C,'C'],[D,'D']].forEach(([P,lbl])=>{ + s+=``; + s+=`${lbl}`; + }); + s+=``; + document.getElementById('p15-proof2-svg').innerHTML=s; + } + function show(){ const st=steps[step]; document.getElementById('p15-proof2-step').innerHTML=st.text; renderMath(document.getElementById('p15-proof2-step')); draw(st.h); document.getElementById('p15-proof2-next').textContent=step{ if(step{step=0;show();document.getElementById('p15-proof2-next').disabled=false;}); + show(); + })(); + + /* == Тренажёр == */ + (function(){ + const tasks=[ + {q:'Равнобедренная трапеция, $\\angle A = 70°$. Найди $\\angle B$.', ans:70, hint:'∠B = ∠A = 70°'}, + {q:'Равнобедренная трапеция, $\\angle A = 70°$. Найди $\\angle D$.', ans:110, hint:'∠A + ∠D = 180°, ∠D = 180−70 = 110°'}, + {q:'Диагональ равнобедренной трапеции $AC = 15$. Найди $BD$.', ans:15, hint:'AC = BD = 15 (диагонали равны)'}, + {q:'Равнобедренная трапеция, $\\angle C = 65°$. Найди $\\angle D$.', ans:65, hint:'∠C = ∠D = 65°'}, + {q:'Равнобедренная трапеция, $\\angle B = 120°$. Найди $\\angle C$.', ans:60, hint:'∠B + ∠C = 180°, ∠C = 60°'}, + ]; + let idx=0,score=0; + function show(){ document.getElementById('p15-tr-i').textContent=idx+1; document.getElementById('p15-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p15-tr-task')); document.getElementById('p15-tr-ans').value=''; document.getElementById('p15-tr-fb').style.display='none'; } + document.getElementById('p15-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p15-tr-score').textContent=0;show();}); + document.getElementById('p15-tr-go').addEventListener('click',()=>{ + const ans=+document.getElementById('p15-tr-ans').value; const fb=document.getElementById('p15-tr-fb'); + if(ans===tasks[idx].ans){ score++;document.getElementById('p15-tr-score').textContent=score;addXp(3,'p15-train');bumpProgress('p15',5); if(idxshow(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p15-train-all');} } + else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint); + }); + document.getElementById('p15-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p15-tr-go').click();}); + show(); + })(); + + /* == DnD == */ + (function(){ + const items=[ + {id:'p1',html:'Только одна пара параллельных сторон', ans:'any'}, + {id:'p2',html:'Сумма углов при боковой стороне = 180°', ans:'any'}, + {id:'p3',html:'Боковые стороны равны', ans:'iso'}, + {id:'p4',html:'Диагонали равны', ans:'iso'}, + {id:'p5',html:'Углы при каждом основании равны', ans:'iso'}, + {id:'p6',html:'Все углы 90°', ans:'rect'}, + ]; + const sorter=setupSorter({poolId:'p15-dnd-pool',scopeSelector:'#p15-dnd-wrap',items,cats:['any','iso','rect']}); + document.getElementById('p15-dnd-reset').addEventListener('click',()=>{sorter.reset();document.getElementById('p15-dnd-fb').style.display='none';}); + document.getElementById('p15-dnd-check').addEventListener('click',()=>{ + let ok=0; items.forEach(it=>{if(sorter.placed[it.id]===it.ans)ok++;}); + const fb=document.getElementById('p15-dnd-fb'); + if(ok===items.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p15-dnd');bumpProgress('p15',15);} + else feedback(fb,false,'Верно: '+ok+' из '+items.length+'.'); + }); + })(); + + /* == Босс §15 == */ + (function(){ + const tasks=[ + {q:'Равнобедренная трапеция, $\\angle A = 55°$. Найди $\\angle D$.', ans:125, hint:'∠A+∠D=180°, ∠D=125°'}, + {q:'$BD = 17$ в равнобедренной трапеции. Найди $AC$.', ans:17, hint:'AC = BD = 17'}, + {q:'Равнобедренная трапеция, $\\angle C = 40°$. Найди $\\angle A$.', ans:140, hint:'∠C+∠B=180°, ∠A=∠B=180−40=140°'}, + {q:'Равнобедренная трапеция, $\\angle A = 80°$. Найди $\\angle C$.', ans:100, hint:'∠A+∠D=180°→∠D=100°; ∠C=∠D=100°'}, + ]; + const bossBox=document.getElementById('p15-boss-tasks'); + bossBox.innerHTML=tasks.map((t,i)=>` +
+
${t.q}
+
+ + +
+ +
`).join(''); + window.p15BossSolved=new Set(); + })(); + renderMath(box); +} +function buildP16(){ + const box = document.getElementById('p16-body'); + let html = ''; + + html += makeCard('theory','Признаки равнобедренной трапеции','16.1',` +

Признак 1. Если в трапеции углы при одном из оснований равны, то она является равнобедренной.

+

Признак 2. Если в трапеции диагонали равны, то она является равнобедренной.

`); + + html += makeCard('rule','Доказательство признака 1','16.2',` +

Дано: трапеция $ABCD$, $AD \\parallel BC$, $\\angle A = \\angle B$. Доказать: $AD = BC$ (т.е. трапеция равнобедренная).

+

Через $C$ проведём прямую, параллельную $BD$, до пересечения с $AD$ в точке $E$. $BDCE$ — параллелограмм, $BE = CD$, $CE = BD$.

+

В $\\triangle AEC$: $\\angle A = \\angle AEC$ (как внутренние односторонние при $BC \\parallel AE$, но $\\angle AEC = \\angle B = \\angle A$) $\\Rightarrow \\triangle AEC$ — равнобедренный, $AE = AC$... Итог: $AD = BC$. $\\square$

`); + + html += makeCard('rule','Доказательство признака 2','16.3',` +

Дано: трапеция $ABCD$, $AD \\parallel BC$, $AC = BD$. Доказать: $AD = BC$.

+

Рассмотрим $\\triangle ADB$ и $\\triangle BCA$: $AD = BC$ нужно доказать... применим метод от противного или через высоты.

+

Из $A$ и $B$ опустим высоты $AH_1$, $BH_2$. В $\\triangle ACH_1$ и $\\triangle BDH_2$: $AC = BD$ (дано), $AH_1 = BH_2$ (высоты), значит $CH_1 = DH_2$. Откуда $AD = BC$. $\\square$

`); + + /* INTERACTIVE 1: SVG признак 1 — равные углы */ + html += `
+
ИНТЕРАКТИВ 1
Признак 1: равные углы при основании → равнобедренная
+
Меняй угол слайдером. При равенстве углов A и B индикатор загорается.
+
+ + +
+
+
+
`; + + /* INTERACTIVE 2: SVG признак 2 — равные диагонали */ + html += `
+
ИНТЕРАКТИВ 2
Признак 2: равные диагонали → равнобедренная
+
Тащи вершины трапеции. Когда диагонали становятся равны — трапеция равнобедренная (индикатор).
+
+
+
`; + + /* INTERACTIVE 3: Доказательство признака 1 */ + html += `
+
ИНТЕРАКТИВ 3
Доказательство признака 1 пошагово
+
+
+
+ + +
+
`; + + /* INTERACTIVE 4: Mini-quiz */ + html += `
+
ИНТЕРАКТИВ 4
Верно или неверно? — Признаки трапеции
+
+
+ +
`; + + /* INTERACTIVE 5: Тренажёр */ + html += `
+
ИНТЕРАКТИВ 5
Тренажёр: равнобедренная ли трапеция?
+
Задача 1 / 5Очки: 0
+
+
+ + + +
+ +
`; + + /* INTERACTIVE 6: Босс §16 */ + html += `
+
БОСС §16
Итоговые задачи
+
+5 XP за каждую верную задачу.
+
+
`; + + html += `
+ +
`; + + html += secNav('p15','final1'); + box.innerHTML = html; + + /* == Признак 1: слайдеры углов == */ + (function(){ + function drawSign1(){ + const angA=+document.getElementById('p16-angA-sl').value; + const angB=+document.getElementById('p16-angB-sl').value; + document.getElementById('p16-angA-val').textContent=angA; + document.getElementById('p16-angB-val').textContent=angB; + const W=380, H=200; + const botY=170, Ax=50, Bx=330; + // Compute top vertices from angles + const radA=(180-angA)*Math.PI/180, radB=(180-angB)*Math.PI/180; + const height=100; + const Dx=Ax+height/Math.tan(radA), Dy=botY-height; + const Cx=Bx-height/Math.tan(radB), Cy=botY-height; + let s=``; + const A2={x:Ax,y:botY}, B2={x:Bx,y:botY}, C2={x:Math.max(20,Math.min(W-20,Cx)),y:Math.max(20,Math.min(H-20,Cy))}, D2={x:Math.max(20,Math.min(W-20,Dx)),y:Math.max(20,Math.min(H-20,Dy))}; + s+=``; + // angle arc A + const arcR=20; + function arcSVG(O,p1,p2,col){ const a1=Math.atan2(p1.y-O.y,p1.x-O.x),a2=Math.atan2(p2.y-O.y,p2.x-O.x); return ``; } + s+=arcSVG(A2,D2,B2,'#ef4444'); s+=arcSVG(B2,A2,C2,'#10b981'); + // labels + s+=`A`; + s+=`${angA}°`; + s+=`B`; + s+=`${angB}°`; + s+=`D`; + s+=`C`; + s+=``; + document.getElementById('p16-sign1-svg').innerHTML=s; + const eq=Math.abs(angA-angB)<=1; + const ind=document.getElementById('p16-sign1-ind'); + if(eq){ ind.style.background='var(--ok-bg,#d1fae5)'; ind.style.color='var(--ok,#059669)'; ind.textContent='Углы равны (∠A = ∠B) — трапеция равнобедренная!'; addXp(2,'p16-sign1'); } + else { ind.style.background='var(--pri-soft)'; ind.style.color='var(--muted)'; ind.textContent=`∠A = ${angA}°, ∠B = ${angB}° — не равны, трапеция произвольная.`; } + } + document.getElementById('p16-angA-sl').addEventListener('input',drawSign1); + document.getElementById('p16-angB-sl').addEventListener('input',drawSign1); + drawSign1(); + })(); + + /* == Признак 2: draggable трапеция с диагоналями == */ + (function(){ + const W=400, H=260; + let A={x:50,y:220}, B={x:350,y:220}, C={x:270,y:60}, D={x:130,y:60}; + function dist(P,Q){ return Math.hypot(Q.x-P.x,Q.y-P.y); } + + function redraw2(){ + const ac=dist(A,C), bd=dist(B,D); + const eq=Math.abs(ac-bd)<6; + let s=``; + s+=``; + s+=``; + s+=``; + s+=`AC=${ac.toFixed(1)}`; + s+=`BD=${bd.toFixed(1)}`; + const vNames=['A','B','C','D'], vPts=[A,B,C,D]; + vPts.forEach((V,i)=>{ + s+=``; + s+=``; + const offx=[-14,12,12,-14][i], offy=[14,14,-12,-12][i]; + s+=`${vNames[i]}`; + }); + s+=``; + const wrap=document.getElementById('p16-sign2-svg'); wrap.innerHTML=s; + const svgEl=wrap.querySelector('svg'); + svgEl.querySelectorAll('.p16-vd2').forEach(el=>{ + el.addEventListener('pointerdown',ev=>{ + if(ev.button!==undefined&&ev.button!==0) return; + const vname=el.dataset.v; + try{el.setPointerCapture(ev.pointerId);}catch(e){} + function onMove(e){ + const rect=svgEl.getBoundingClientRect(); + const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*W/rect.width)); + const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*H/rect.height)); + if(vname==='A'){ A={x:nx,y:ny}; B.y=ny; } + else if(vname==='B'){ B={x:nx,y:ny}; A.y=ny; } + else if(vname==='C'){ C={x:nx,y:ny}; D.y=ny; } + else if(vname==='D'){ D={x:nx,y:ny}; C.y=ny; } + redraw2(); + } + function onUp(){ el.removeEventListener('pointermove',onMove); el.removeEventListener('pointerup',onUp); el.removeEventListener('pointercancel',onUp); } + el.addEventListener('pointermove',onMove); el.addEventListener('pointerup',onUp); el.addEventListener('pointercancel',onUp); + }); + }); + const ind=document.getElementById('p16-sign2-ind'); + if(eq){ ind.style.background='var(--ok-bg,#d1fae5)'; ind.style.color='var(--ok,#059669)'; ind.textContent='AC ≈ BD — диагонали равны → трапеция равнобедренная!'; addXp(2,'p16-sign2'); } + else { ind.style.background='var(--pri-soft)'; ind.style.color='var(--muted)'; ind.textContent=`AC = ${ac.toFixed(1)}, BD = ${bd.toFixed(1)} — не равны, трапеция произвольная.`; } + } + redraw2(); + })(); + + /* == Доказательство признака 1 == */ + (function(){ + const A={x:50,y:210}, B={x:290,y:210}, C={x:220,y:70}, D={x:110,y:70}; + const Cx2=A.x+(B.x-D.x), Cy2=A.y+(B.y-D.y); + const E={x:Math.min(320, Cx2), y:A.y}; + const steps=[ + {text:'Дано: трапеция $ABCD$, $AD \\parallel BC$, $\\angle DAB = \\angle CBA$. Доказать: $AB = CD$ (равнобедренная).', h:'base'}, + {text:'Шаг 1. Через $C$ проведём прямую $CE \\parallel AB$ до пересечения с $AD$ в точке $E$.', h:'aux'}, + {text:'Шаг 2. $ABCE$ — параллелограмм ($AB \\parallel CE$, $BC \\parallel AE$). Значит $AB = CE$ и $BC = AE$.', h:'para'}, + {text:'Шаг 3. $\\angle DAB = \\angle CBA = \\angle AEC$ (как накрест лежащие при $AB \\parallel CE$). Тогда $\\triangle AEC$ — равнобедренный: $AE = CE = AB$.', h:'iso'}, + {text:'Вывод. $ED = AD - AE$, $DC = CE = AB$... Итог: $AB = CD$. Трапеция равнобедренная. $\\square$', h:'done'}, + ]; + let step=0; + function draw(h){ + let s=``; + s+=``; + if(h==='aux'||h==='para'||h==='iso'||h==='done'){ + s+=``; + s+=``; + s+=`E`; + } + if(h==='para'||h==='done'){ + s+=``; + } + if(h==='iso'||h==='done'){ + s+=``; + } + [[A,'A'],[B,'B'],[C,'C'],[D,'D']].forEach(([P,lbl])=>{ + s+=``; + s+=`${lbl}`; + }); + s+=``; + document.getElementById('p16-proof-svg').innerHTML=s; + } + function show(){ const st=steps[step]; document.getElementById('p16-proof-step').innerHTML=st.text; renderMath(document.getElementById('p16-proof-step')); draw(st.h); document.getElementById('p16-proof-next').textContent=step{ if(step{step=0;show();document.getElementById('p16-proof-next').disabled=false;}); + show(); + })(); + + /* == Mini-quiz == */ + (function(){ + const qs=[ + {text:'Если в трапеции ∠A = ∠B, то она равнобедренная.', ans:true}, + {text:'Если в трапеции ∠A = ∠D, то она равнобедренная.', ans:false}, + {text:'Если в трапеции AC = BD, то она равнобедренная.', ans:true}, + {text:'Любая трапеция с равными диагоналями — прямоугольная.', ans:false}, + {text:'Признак 1 и признак 2 — два разных способа доказать одно и то же.', ans:true}, + ]; + const list=document.getElementById('p16-quiz-list'); + list.innerHTML=qs.map((q,i)=>` +
+
${q.text}
+
+ + +
+
`).join(''); + const sel={}; + list.querySelectorAll('.p16-qbtn').forEach(btn=>{ + btn.addEventListener('click',()=>{ + const i=btn.dataset.i; + sel[i]=btn.dataset.v==='true'; + list.querySelectorAll(`.p16-qbtn[data-i="${i}"]`).forEach(b=>b.className='btn p16-qbtn'); + btn.className='btn primary p16-qbtn'; + }); + }); + document.getElementById('p16-quiz-check').addEventListener('click',()=>{ + let ok=0; + qs.forEach((q,i)=>{ if(sel[i]===q.ans) ok++; }); + const fb=document.getElementById('p16-quiz-fb'); + if(ok===qs.length){feedback(fb,true,'Все верно! +5 XP');addXp(5,'p16-quiz');bumpProgress('p16',15);} + else feedback(fb,false,'Верно: '+ok+' из '+qs.length+'. Признак: углы при ОДНОМ ОСНОВАНИИ (A и B), а не при одной боковой стороне.'); + }); + document.getElementById('p16-quiz-reset').addEventListener('click',()=>{ Object.keys(sel).forEach(k=>delete sel[k]); list.querySelectorAll('.p16-qbtn').forEach(b=>b.className='btn p16-qbtn'); document.getElementById('p16-quiz-fb').style.display='none'; }); + })(); + + /* == Тренажёр == */ + (function(){ + const tasks=[ + {q:'В трапеции $\\angle A = \\angle B = 75°$. Равнобедренная ли она? (1 — да, 0 — нет)', ans:1, hint:'Да, по признаку 1 (углы при основании AB равны)'}, + {q:'В трапеции $\\angle A = 80°$, $\\angle D = 80°$. Равнобедренная ли она? (1 — да, 0 — нет)', ans:0, hint:'Нет: ∠A и ∠D — при разных основаниях, это не признак'}, + {q:'В трапеции $AC = BD = 13$. Равнобедренная ли она? (1 — да, 0 — нет)', ans:1, hint:'Да, по признаку 2 (диагонали равны)'}, + {q:'В трапеции $\\angle A = 65°$, $\\angle B = 65°$. Найди $\\angle D$.', ans:115, hint:'∠A+∠D=180°, ∠D=115°'}, + {q:'Признак равнобедренной трапеции — равные (1) диагонали или (2) медианы? Введи 1 или 2.', ans:1, hint:'Признак — равные диагонали'}, + ]; + let idx=0,score=0; + function show(){ document.getElementById('p16-tr-i').textContent=idx+1; document.getElementById('p16-tr-task').innerHTML=tasks[idx].q; renderMath(document.getElementById('p16-tr-task')); document.getElementById('p16-tr-ans').value=''; document.getElementById('p16-tr-fb').style.display='none'; } + document.getElementById('p16-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p16-tr-score').textContent=0;show();}); + document.getElementById('p16-tr-go').addEventListener('click',()=>{ + const ans=+document.getElementById('p16-tr-ans').value; const fb=document.getElementById('p16-tr-fb'); + if(ans===tasks[idx].ans){ score++;document.getElementById('p16-tr-score').textContent=score;addXp(3,'p16-train');bumpProgress('p16',5); if(idxshow(),900);}else{feedback(fb,true,'Все задачи! +5 XP');addXp(5,'p16-train-all');} } + else feedback(fb,false,'Неверно. Подсказка: '+tasks[idx].hint); + }); + document.getElementById('p16-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p16-tr-go').click();}); + show(); + })(); + + /* == Босс §16 == */ + (function(){ + const tasks=[ + {q:'В трапеции $\\angle A = \\angle B = 62°$. Найди $\\angle C$.', ans:118, hint:'∠B+∠C=180°, ∠C=118°'}, + {q:'В трапеции $AC = 20$, $BD = 20$. Равнобедренная ли она? (1 — да, 0 — нет)', ans:1, hint:'Да, диагонали равны — признак 2'}, + {q:'В трапеции $\\angle D = 55°$. Если она равнобедренная, найди $\\angle C$.', ans:55, hint:'∠C = ∠D = 55° в равнобедренной трапеции'}, + {q:'$\\angle A = 72°$, $\\angle B = 72°$. Найди $\\angle A + \\angle D$.', ans:180, hint:'∠A+∠D = 180° (свойство трапеции)'}, + ]; + const bossBox=document.getElementById('p16-boss-tasks'); + bossBox.innerHTML=tasks.map((t,i)=>` +
+
${t.q}
+
+ + +
+ +
`).join(''); + window.p16BossSolved=new Set(); + })(); + renderMath(box); +} function buildFinal1stub(){ document.getElementById('final1-body').innerHTML = '

Финал главы 1 — Волна 1: боссы и итоги появятся в следующем обновлении.

' + secNav('p16',null); }