diff --git a/frontend/textbooks/geometry_11_ch1.html b/frontend/textbooks/geometry_11_ch1.html index 3a77c31..03f367a 100644 --- a/frontend/textbooks/geometry_11_ch1.html +++ b/frontend/textbooks/geometry_11_ch1.html @@ -392,7 +392,7 @@ function buildParaSelector(){ } const BUILT=new Set(); -const BUILDERS = { p1:()=>buildP1(), p2:()=>buildStub('p2'), final1:()=>buildStub('final1') }; +const BUILDERS = { p1:()=>buildP1(), p2:()=>buildP2(), final1:()=>buildStub('final1') }; function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } } function goTo(id){ STATE.current=id; ensureBuilt(id); @@ -408,13 +408,13 @@ function goTo(id){ const SIDEBARS = { p1:{title:"Шпаргалка § 1", rows:[["Тема","Призма"],["Прямая","$S_{бок}=P_{осн}\\\\cdot h$"],["Наклонная","$S_{бок}=P_{пер}\\\\cdot l$"],["Объём","$V=S_{осн}\\\\cdot h$"],["Диагональ пар.","$d=\\\\sqrt{a^2+b^2+c^2}$"]]}, - p2:{title:"Шпаргалка § 2", rows:[["Тема", "Цилиндр"],["Формула","$S_{бок}=2\\\\pi Rh$, $V=\\\\pi R^2h$"]]}, + p2:{title:"Шпаргалка § 2", rows:[["Тема", "Цилиндр"],["$S_{осн}$","$\\\\pi R^2$"],["$S_{бок}$","$2\\\\pi Rh$"],["$S_{полн}$","$2\\\\pi R(R+h)$"],["$V$","$\\\\pi R^2 h$"],["Развёртка","прямоуг. $2\\\\pi R \\\\times h$"],["Осевое сеч.","прямоуг. $2R \\\\times h$"],["Наклон. сеч.","эллипс, $a=R/\\\\cos\\\\alpha$, $b=R$"]]}, final1:{title:"Финал раздела 1", rows:[["§ 1–§ 2","теория раздела 1"],["Награда","+50 XP"]]} }; const TIPS=[ {sec:'p1',html:"§ 1 «Призма» — крути 3D-модель в интерактиве 1, проверь формулы в калькуляторе. Главное: $V=S_{осн}\\\\cdot h$, $S_{бок}=P_{осн}\\\\cdot h$ (для прямой)."}, - {sec:'p2',html:"§ 2 «Цилиндр» — содержание в разработке. $S_{бок}=2\\\\\\\\pi Rh$, $V=\\\\\\\\pi R^2h$"}, + {sec:'p2',html:"§ 2 «Цилиндр» — крути 3D-модель в интерактиве 1, разбирай сечения в IV2 (круг/прямоугольник/эллипс). Главное: $S_{бок}=2\\\\\\\\pi Rh$, $V=\\\\\\\\pi R^2 h$, развёртка боковой поверхности — прямоугольник $2\\\\\\\\pi R \\\\\\\\times h$."}, {sec:'final1',html:"Финал раздела 1 — интегрированные задачи по разделу."} ]; @@ -989,6 +989,423 @@ function buildP1(){ wireReadBtn('p1'); } +/* ===== §2 «Цилиндр» — Wave 2 ===== */ +function buildP2(){ + const box = document.getElementById('p2-body'); + if(!box) return; + let html = ''; + + /* === ТЕОРИЯ === */ + + html += makeCard('theory', 'Определение и элементы', '§ 2.1', + '

Цилиндр (точнее — прямой круговой цилиндр) — тело, полученное вращением прямоугольника вокруг одной из его сторон.

' + + '

Элементы цилиндра:

' + + '' + + '

Цилиндр можно рассматривать как предельный случай правильной $n$-угольной призмы: если число сторон основания $n \\to \\infty$, призма «выпрямляется» в цилиндр, а её боковая поверхность — в боковую поверхность цилиндра.

'); + + html += makeCard('rule', 'Площадь поверхности и объём', '§ 2.2', + '

Основные формулы цилиндра радиуса $R$ и высоты $h$:

' + + '

$$S_{осн}=\\pi R^2 \\qquad S_{бок}=2\\pi R h$$

' + + '

$$S_{полн}=2\\pi R(R+h) \\qquad V=\\pi R^2 h$$

' + + '

Развёртка боковой поверхности — прямоугольник со сторонами $2\\pi R$ (длина окружности основания) и $h$ (высота). Отсюда $S_{бок}=2\\pi R \\cdot h$.

' + + '
Пример: $R=3$, $h=5$
' + + '' + + '
'); + + html += makeCard('example', 'Сечения цилиндра', '§ 2.3', + '

Какие фигуры получаются в сечении цилиндра плоскостью:

' + + '' + + '

Касательная плоскость к цилиндру проходит через одну образующую и не пересекает цилиндр в других точках. Через любую образующую можно провести ровно одну касательную плоскость; она перпендикулярна осевому сечению, проходящему через эту образующую.

' + + '
Почему наклонное сечение — эллипс?
' + + '

Пусть плоскость $\\pi$ наклонена к основанию под углом $\\alpha$. Спроецируем сечение на плоскость основания вдоль оси цилиндра. Проекция — круг радиуса $R$ (граница цилиндра, видимая сверху).

' + + '

В направлении линии пересечения с основанием размеры сохраняются ($b=R$), а в перпендикулярном направлении сечение «растянуто» в $1/\\cos\\alpha$ раз — получается $a=R/\\cos\\alpha$. Это и есть эллипс с полуосями $R$ и $R/\\cos\\alpha$.

' + + '
'); + + /* === ИНТЕРАКТИВ 1 — 3D-конструктор === */ + html += '
' + + '
3D · конструктор
Цилиндр $R \\times h$
' + + '
Меняй радиус $R$ и высоту $h$. Вращай мышью или выбирай вид. После 4 разных конфигураций — +10 XP.
' + + '
' + + '' + + '' + + '
' + + '
' + + '' + + '' + + '' + + '' + + '
' + + '
' + + '
' + + '$P_{осн}=$' + + '$S_{осн}=$' + + '$S_{бок}=$' + + '$S_{полн}=$' + + '$V=$' + + '
' + + '
Конфигураций изучено: 0 / 4
' + + '
'; + + /* === ИНТЕРАКТИВ 2 — Сечения === */ + html += '
' + + '
визуализатор
4 типа сечений цилиндра
' + + '
Выбери тип сечения и крути ползунки. После просмотра всех 4 типов — +10 XP.
' + + '
' + + '' + + '' + + '' + + '' + + '' + + '
' + + '
' + + '
' + + '
Типов сечений просмотрено: 0 / 4
' + + '
'; + + /* === ИНТЕРАКТИВ 3 — Квикфайр === */ + html += '
' + + '
квикфайр · 6 вопросов
Какая фигура в сечении?
' + + '
Прочитай описание секущей плоскости и выбери фигуру. После 6 верных — +15 XP.
' + + '
' + + '
Верно: 0 / 6
' + + '
'; + + /* === ИНТЕРАКТИВ 4 — Тренажёр === */ + html += '
' + + '
тренажёр · 6 задач
$V$ и $S$ цилиндра
' + + '
Введи числовой ответ. Допуск $\\pm 0{,}05$. Используй $\\pi \\approx 3{,}14$.
' + + '
' + + '
Решено: 0 / 6
' + + '
'; + + html += secNavFor('p2'); + html += readButton('p2'); + + box.innerHTML = html; + renderMath(box); + + /* ===== JS-логика интерактивов ===== */ + + /* IV1 — 3D конструктор */ + (function(){ + if(!window.G3D) return; + const svg = document.getElementById('p2-iv1-svg'); + const elR = document.getElementById('p2-iv1-R'); + const elH = document.getElementById('p2-iv1-h'); + const vR = document.getElementById('p2-iv1-R-v'); + const vH = document.getElementById('p2-iv1-h-v'); + const oP = document.getElementById('p2-iv1-P'); + const oSb = document.getElementById('p2-iv1-Sb'); + const oSs = document.getElementById('p2-iv1-Ss'); + const oSt = document.getElementById('p2-iv1-St'); + const oV = document.getElementById('p2-iv1-V'); + const oCnt = document.getElementById('p2-iv1-cnt'); + if(!svg) return; + const scene = G3D.createScene({W:480, H:400, scale:50, camDist:8, rotX:-0.35, rotY:0.7}); + const seen = new Set(); + let xpGiven = false; + + function draw(){ + const R = +elR.value, h = +elH.value; + vR.textContent = R.toFixed(1); + vH.textContent = h.toFixed(1); + const mesh = G3D.cylinderMesh(R, h, 32); + const M = G3D.buildRotMatrix(scene); + svg.innerHTML = G3D.renderMesh(mesh, M, scene, { + fillBase:'rgba(252,231,243,.55)', + fillSide:'rgba(219,234,254,.55)', + strokeVisible:'#0f172a', + strokeHidden:'#94a3b8' + }); + const P = 2*Math.PI*R; + const Sb = Math.PI*R*R; + const Ss = 2*Math.PI*R*h; + const St = 2*Math.PI*R*(R+h); + const V = Math.PI*R*R*h; + oP.textContent = P.toFixed(2); + oSb.textContent = Sb.toFixed(2); + oSs.textContent = Ss.toFixed(2); + oSt.textContent = St.toFixed(2); + oV.textContent = V.toFixed(2); + const key = R.toFixed(1)+'|'+h.toFixed(1); + seen.add(key); + oCnt.textContent = Math.min(seen.size, 4); + if(seen.size >= 4 && !xpGiven){ + xpGiven = true; + addXp(10, 'p2-iv1'); + bumpProgress('p2', 15); + const note = document.createElement('div'); + note.className = 'feedback ok'; + note.innerHTML = '✓ +10 XP за изучение 4 разных цилиндров!'; + note.style.cssText = 'display:block;margin-top:8px'; + const host = document.getElementById('p2-iv1'); + if(host) host.appendChild(note); + setTimeout(function(){ try{ note.remove(); }catch(e){} }, 3000); + } + } + draw(); + G3D.attachOrbit(svg, scene, draw); + [elR, elH].forEach(function(el){ el.addEventListener('input', draw); }); + document.querySelectorAll('#p2-iv1 .g3d-tools .btn').forEach(function(b){ + b.addEventListener('click', function(){ G3D.presetView(scene, b.dataset.view, draw); }); + }); + })(); + + /* IV2 — Сечения */ + (function(){ + const svg = document.getElementById('p2-iv2-svg'); + const info = document.getElementById('p2-iv2-info'); + const elT = document.getElementById('p2-iv2-t'); + const elR = document.getElementById('p2-iv2-R'); + const elH = document.getElementById('p2-iv2-h'); + const elD = document.getElementById('p2-iv2-d'); + const elA = document.getElementById('p2-iv2-a'); + const vT = document.getElementById('p2-iv2-t-v'); + const vR = document.getElementById('p2-iv2-R-v'); + const vH = document.getElementById('p2-iv2-h-v'); + const vD = document.getElementById('p2-iv2-d-v'); + const vA = document.getElementById('p2-iv2-a-v'); + const wrapD = document.getElementById('p2-iv2-d-wrap'); + const wrapA = document.getElementById('p2-iv2-a-wrap'); + const oCnt = document.getElementById('p2-iv2-cnt'); + if(!svg) return; + + const TYPE_NAMES = { + 1:'Параллельное основанию', + 2:'Осевое', + 3:'Параллельное оси (на расстоянии $d$ от оси)', + 4:'Наклонное (под углом $\\alpha$ к основанию)' + }; + const seenTypes = new Set(); + let xpGiven = false; + + function draw(){ + const t = +elT.value; + const R = +elR.value, h = +elH.value; + let d = +elD.value, a = +elA.value; + vT.textContent = t; + vR.textContent = R.toFixed(1); + vH.textContent = h.toFixed(1); + vD.textContent = d.toFixed(1); + vA.textContent = a; + /* Динамически ограничиваем d ≤ R - 0.1 */ + elD.max = (R - 0.1).toFixed(1); + if(d > +elD.max){ d = +elD.max; elD.value = d; vD.textContent = d.toFixed(1); } + + wrapD.style.display = (t === 3) ? '' : 'none'; + wrapA.style.display = (t === 4) ? '' : 'none'; + + /* Масштаб: используем единичный масштаб, чтобы вписать в 380x280 */ + const W = 380, H = 280; + const cx = W/2, cy = H/2; + let body = ''; + let infoHtml = '

Тип '+t+': '+TYPE_NAMES[t]+'

'; + + if(t === 1){ + /* Круг радиуса R */ + const sc = Math.min((W-60)/(2*R), (H-60)/(2*R)); + const rPx = R*sc; + body += ''; + /* радиус */ + body += ''; + body += ''; + body += 'R = '+R.toFixed(1)+''; + infoHtml += '

Фигура — круг радиуса $R='+R.toFixed(1)+'$.

'; + infoHtml += '

Площадь сечения: $S=\\pi R^2 \\approx '+(Math.PI*R*R).toFixed(2)+'$.

'; + } else if(t === 2){ + /* Осевое — прямоугольник 2R × h */ + const sc = Math.min((W-60)/(2*R), (H-60)/h); + const wPx = 2*R*sc, hPx = h*sc; + const x0 = cx - wPx/2, y0 = cy - hPx/2; + body += ''; + /* ось */ + body += ''; + body += 'ось'; + body += '2R = '+(2*R).toFixed(1)+''; + body += 'h = '+h.toFixed(1)+''; + infoHtml += '

Фигура — прямоугольник со сторонами $2R='+(2*R).toFixed(1)+'$ и $h='+h.toFixed(1)+'$.

'; + infoHtml += '

Площадь сечения: $S=2Rh = '+(2*R*h).toFixed(2)+'$.

'; + } else if(t === 3){ + /* Параллельное оси, не осевое — прямоугольник со сторонами 2√(R²-d²) и h */ + const w = 2*Math.sqrt(Math.max(0, R*R - d*d)); + const sc = Math.min((W-60)/(2*R), (H-60)/h); + const wPx = w*sc, hPx = h*sc; + /* нарисуем «пунктиром» полный осевой прямоугольник 2R×h для сравнения и закрасим нашу полосу */ + const fullW = 2*R*sc; + const fx0 = cx - fullW/2, fy0 = cy - hPx/2; + body += ''; + const x0 = cx - wPx/2, y0 = cy - hPx/2; + body += ''; + body += '2√(R²−d²) = '+w.toFixed(2)+''; + body += 'h = '+h.toFixed(1)+''; + infoHtml += '

Фигура — прямоугольник со сторонами $2\\sqrt{R^2-d^2}\\approx '+w.toFixed(2)+'$ и $h='+h.toFixed(1)+'$.

'; + infoHtml += '

При $d='+d.toFixed(1)+'$, $R='+R.toFixed(1)+'$: ширина = $2\\sqrt{'+(R*R).toFixed(2)+'-'+(d*d).toFixed(2)+'}='+w.toFixed(2)+'$.

'; + infoHtml += '

Площадь сечения: $S='+w.toFixed(2)+' \\cdot '+h.toFixed(1)+' = '+(w*h).toFixed(2)+'$.

'; + } else { + /* Наклонное — эллипс с полуосями R и R/cos(α) */ + const alpha = a * Math.PI/180; + const aMaj = R / Math.cos(alpha); /* большая полуось */ + const sc = Math.min((W-60)/(2*aMaj), (H-60)/(2*R)); + const aPx = aMaj*sc, bPx = R*sc; + body += ''; + /* большая ось */ + body += ''; + /* малая ось */ + body += ''; + body += 'a = '+aMaj.toFixed(2)+''; + body += 'b = '+R.toFixed(1)+''; + infoHtml += '

Фигура — эллипс с полуосями $b=R='+R.toFixed(1)+'$ и $a=R/\\cos\\alpha = '+R.toFixed(1)+'/\\cos '+a+'° \\approx '+aMaj.toFixed(2)+'$.

'; + infoHtml += '

Площадь эллипса: $S=\\pi ab \\approx '+(Math.PI*R*aMaj).toFixed(2)+'$.

'; + } + svg.innerHTML = body; + info.innerHTML = infoHtml; + try{ renderMath(info); }catch(e){} + + seenTypes.add(t); + oCnt.textContent = seenTypes.size; + if(seenTypes.size >= 4 && !xpGiven){ + xpGiven = true; + addXp(10, 'p2-iv2'); + bumpProgress('p2', 15); + const note = document.createElement('div'); + note.className = 'feedback ok'; + note.innerHTML = '✓ +10 XP за разбор всех 4 типов сечений!'; + note.style.cssText = 'display:block;margin-top:8px'; + const host = document.getElementById('p2-iv2'); + if(host) host.appendChild(note); + setTimeout(function(){ try{ note.remove(); }catch(e){} }, 3000); + } + } + draw(); + [elT, elR, elH, elD, elA].forEach(function(el){ el.addEventListener('input', draw); }); + })(); + + /* IV3 — Квикфайр */ + (function(){ + const tasks = [ + { q:'Сечение цилиндра плоскостью, параллельной основанию:', a:'circle' }, + { q:'Сечение цилиндра осевой плоскостью:', a:'rect' }, + { q:'Сечение цилиндра плоскостью, перпендикулярной оси:', a:'circle' }, + { q:'Сечение цилиндра плоскостью, параллельной оси, но не проходящей через ось:', a:'rect' }, + { q:'Сечение цилиндра плоскостью, наклонной к основанию под углом $60°$ (пересекает все образующие):', a:'ellipse' }, + { q:'Сечение цилиндра плоскостью, проходящей через ось и наклонённой к основанию:', a:'rect' } + ]; + const LABELS = {circle:'Круг', rect:'Прямоугольник', ellipse:'Эллипс'}; + const area = document.getElementById('p2-iv3-area'); + const scoreEl = document.getElementById('p2-iv3-score'); + const solved = new Set(); + let xpGiven = false; + + area.innerHTML = tasks.map(function(t, i){ + return '
' + + '
Вопрос '+(i+1)+'. '+t.q+'
' + + '
' + + '' + + '' + + '' + + '
' + + '' + + '
'; + }).join(''); + renderMath(area); + + area.querySelectorAll('button[data-i]').forEach(function(b){ + b.addEventListener('click', function(){ + const i = +b.dataset.i, v = b.dataset.v, t = tasks[i]; + const fb = document.getElementById('p2-iv3-fb-'+i); + if(v === t.a){ + feedback(fb, true, '✓ Верно — '+LABELS[t.a]+'!'); + if(!solved.has(i)){ + solved.add(i); + scoreEl.textContent = solved.size; + if(solved.size === tasks.length && !xpGiven){ + xpGiven = true; + addXp(15, 'p2-iv3'); + bumpProgress('p2', 25); + } + } + } else { + feedback(fb, false, '✗ Неверно. Подсказка: подумай, как плоскость пересекает образующие.'); + } + }); + }); + })(); + + /* IV4 — Тренажёр */ + (function(){ + const PI = 3.14; + const tasks = [ + { q:'Цилиндр $R=2$, $h=5$. Найди $V$ (используй $\\pi\\approx 3{,}14$).', a:+(PI*4*5).toFixed(2), tol:0.05 }, /* 62.80 */ + { q:'Цилиндр $R=3$, $h=4$. Найди $S_{бок}$ ($\\pi\\approx 3{,}14$).', a:+(2*PI*3*4).toFixed(2), tol:0.05 }, /* 75.36 */ + { q:'Цилиндр $R=5$, $h=2$. Найди $S_{полн}$ ($\\pi\\approx 3{,}14$).', a:+(2*PI*5*7).toFixed(2), tol:0.05 }, /* 219.80 */ + { q:'$V$ цилиндра равен $100\\pi$, высота $h=4$. Найди радиус $R$.', a:5, tol:0.05 }, + { q:'Цилиндр $R=2$, $h=3$. Найди длину окружности основания ($\\pi\\approx 3{,}14$).', a:+(2*PI*2).toFixed(2), tol:0.05 }, /* 12.56 */ + { q:'Цилиндр $R=4$ пересечён плоскостью под углом $60°$ к основанию. Найди большую полуось эллипса в сечении.', a:8, tol:0.05 } + ]; + const list = document.getElementById('p2-iv4-list'); + const scoreEl = document.getElementById('p2-iv4-score'); + const solved = new Set(); + let xpGiven = false; + + list.innerHTML = tasks.map(function(t, i){ + return '
' + + '
Задача '+(i+1)+'. '+t.q+'
' + + '
' + + '' + + '' + + '
' + + '' + + '
'; + }).join(''); + renderMath(list); + + list.querySelectorAll('button[data-i]').forEach(function(b){ + b.addEventListener('click', function(){ + const i = +b.dataset.i, t = tasks[i]; + const inp = document.getElementById('p2-iv4-inp-'+i); + const fb = document.getElementById('p2-iv4-fb-'+i); + const raw = (inp.value || '').replace(',', '.').trim(); + const val = parseFloat(raw); + if(!isFinite(val)){ feedback(fb, false, '✗ Введи число'); return; } + if(Math.abs(val - t.a) <= t.tol){ + feedback(fb, true, '✓ Верно!'); + if(!solved.has(i)){ + solved.add(i); + scoreEl.textContent = solved.size; + if(solved.size === tasks.length && !xpGiven){ + xpGiven = true; + addXp(15, 'p2-iv4'); + bumpProgress('p2', 25); + setTimeout(function(){ achievement('p2_done'); }, 400); + } + } + } else { + feedback(fb, false, '✗ Не точно. Пересчитай аккуратно ($\\pi\\approx 3{,}14$).'); + } + }); + }); + })(); + + wireReadBtn('p2'); +} + /* ===== STUB BUILDER — единый для всех параграфов раздела (Phase 0) ===== */ function buildStub(id){