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',
+ '
Цилиндр (точнее — прямой круговой цилиндр ) — тело, полученное вращением прямоугольника вокруг одной из его сторон.
'
+ + 'Элементы цилиндра:
'
+ + ''
+ + 'Основания — два равных круга радиуса $R$, лежащих в параллельных плоскостях. '
+ + 'Ось — отрезок, соединяющий центры оснований. '
+ + 'Образующие — отрезки длины $h$, параллельные оси и соединяющие соответствующие точки окружностей оснований. Все образующие равны и $\\perp$ основаниям. '
+ + 'Боковая поверхность — объединение всех образующих. '
+ + 'Высота $h$ — расстояние между основаниями (= длина образующей). '
+ + 'Радиус $R$ — радиус основания. '
+ + ' '
+ + 'Цилиндр можно рассматривать как предельный случай правильной $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$ '
+ + '
'
+ + '$S_{осн}=9\\pi \\approx 28{,}3$ '
+ + '$S_{бок}=2\\pi\\cdot 3\\cdot 5=30\\pi \\approx 94{,}2$ '
+ + '$S_{полн}=2\\pi\\cdot 3(3+5)=48\\pi \\approx 150{,}8$ '
+ + '$V=\\pi\\cdot 9\\cdot 5=45\\pi \\approx 141{,}4$ '
+ + ' '
+ + '
');
+
+ html += makeCard('example', 'Сечения цилиндра', '§ 2.3',
+ 'Какие фигуры получаются в сечении цилиндра плоскостью:
'
+ + ''
+ + 'Перпендикулярное оси (= параллельное основанию): круг радиуса $R$. '
+ + 'Осевое (через ось): прямоугольник со сторонами $2R$ и $h$. '
+ + 'Параллельное оси, не осевое (на расстоянии $dпрямоугольник со сторонами $2\\sqrt{R^2-d^2}$ и $h$. '
+ + 'Наклонное (плоскость пересекает все образующие, угол с основанием $\\alpha$): эллипс с полуосями $b=R$ (малая) и $a=R/\\cos\\alpha$ (большая). '
+ + ' '
+ + 'Касательная плоскость к цилиндру проходит через одну образующую и не пересекает цилиндр в других точках. Через любую образующую можно провести ровно одну касательную плоскость; она перпендикулярна осевому сечению, проходящему через эту образующую.
'
+ + 'Почему наклонное сечение — эллипс? '
+ + '
Пусть плоскость $\\pi$ наклонена к основанию под углом $\\alpha$. Спроецируем сечение на плоскость основания вдоль оси цилиндра. Проекция — круг радиуса $R$ (граница цилиндра, видимая сверху).
'
+ + '
В направлении линии пересечения с основанием размеры сохраняются ($b=R$), а в перпендикулярном направлении сечение «растянуто» в $1/\\cos\\alpha$ раз — получается $a=R/\\cos\\alpha$. Это и есть эллипс с полуосями $R$ и $R/\\cos\\alpha$.
'
+ + '
');
+
+ /* === ИНТЕРАКТИВ 1 — 3D-конструктор === */
+ html += ''
+ + ''
+ + '
Меняй радиус $R$ и высоту $h$. Вращай мышью или выбирай вид. После 4 разных конфигураций — +10 XP.
'
+ + '
'
+ + '$R$ (радиус):2.0 '
+ + '$h$ (высота):3.0 '
+ + '
'
+ + '
'
+ + 'Изо '
+ + 'Спереди '
+ + 'Сверху '
+ + 'Сбоку '
+ + '
'
+ + '
'
+ + '
'
+ + '$P_{осн}=$— '
+ + '$S_{осн}=$— '
+ + '$S_{бок}=$— '
+ + '$S_{полн}=$— '
+ + '$V=$— '
+ + '
'
+ + '
Конфигураций изучено: 0 / 4
'
+ + '
';
+
+ /* === ИНТЕРАКТИВ 2 — Сечения === */
+ html += '';
+
+ /* === ИНТЕРАКТИВ 3 — Квикфайр === */
+ html += ''
+ + ''
+ + '
Прочитай описание секущей плоскости и выбери фигуру. После 6 верных — +15 XP.
'
+ + '
'
+ + '
Верно: 0 / 6
'
+ + '
';
+
+ /* === ИНТЕРАКТИВ 4 — Тренажёр === */
+ html += ''
+ + ''
+ + '
Введи числовой ответ. Допуск $\\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){