feat(math6): Глава 6 — Наглядная геометрия (§1–§5 + финал)

§1 тела (куб/призма/пирамида/цилиндр/конус) + развёртки: квиз грани/рёбра/
вершины + «какое тело из развёртки»; §2 окружность и круг (слайдер r → C, S
при π=3,14) + тренажёр; §3 виды треугольников по сторонам и по углам
(классификация вычисляется из координат, штрихи равных сторон, метка прямого
угла); §4 центральная симметрия (построй A'); §5 осевая симметрия (Oy/Ox);
финал — 5 боссов. SVG: тела/развёртки/треугольники inline, симметрия на
Math6.plane. Тесты math6: 14/14.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-02 19:48:23 +03:00
parent 09c61d8eed
commit 670ae80124
2 changed files with 392 additions and 0 deletions
+21
View File
@@ -85,6 +85,27 @@ test('ch5: координатная плоскость — интерактив
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | ')); assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
}); });
test('ch6: наглядная геометрия — интерактивы §1–§5 + финал', async () => {
const { doc, errors } = await loadDom('math_6_ch6.html');
const win = doc.defaultView;
assert.ok(doc.querySelector('#p1-fig svg') && doc.querySelector('#p1-q'), 'тела §1');
win.goTo('p2'); await wait(80);
assert.ok(doc.querySelector('#p2-r') && doc.querySelector('#p2-fig svg circle'), 'окружность §2');
assert.ok(doc.querySelector('#p2-out').textContent.indexOf('=') >= 0, 'формулы C, S §2');
win.goTo('p3'); await wait(80);
assert.ok(doc.querySelector('#p3-fig svg polygon'), 'треугольник §3');
assert.ok(doc.querySelectorAll('#p3-iv1 [data-v]').length === 3, 'виды по сторонам §3');
win.goTo('p4'); await wait(80);
assert.ok(doc.querySelector('#p4-fig svg'), 'плоскость симметрии §4');
win.goTo('p5'); await wait(80);
assert.ok(doc.querySelector('#p5-fig svg'), 'плоскость симметрии §5');
win.goTo('final'); await wait(80);
assert.ok(doc.querySelector('#fin-go'), 'арена боссов §6');
win.bumpProgress('final', 100); await wait(20);
assert.ok(win.M6STATE.achievements.has('ch6_done'), 'достижение «Глава 6 пройдена»');
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
});
test('hub: 6 карточек глав', async () => { test('hub: 6 карточек глав', async () => {
const { doc, errors } = await loadDom('math_6_hub.html'); const { doc, errors } = await loadDom('math_6_hub.html');
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | ')); assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
+371
View File
@@ -92,6 +92,377 @@ window.M6 = {
sidebars: {}, tips: [], glossary: [], builders: {}, sidebars: {}, tips: [], glossary: [], builders: {},
footer: 'Интерактивный учебник «Математика 6» · Глава 6 · Наглядная геометрия · LearnSpace' footer: 'Интерактивный учебник «Математика 6» · Глава 6 · Наглядная геометрия · LearnSpace'
}; };
/* ===================== ВСПОМОГАТЕЛЬНОЕ ===================== */
function _ri(a,b){ return a + Math.floor(Math.random()*(b-a+1)); }
function _pick(a){ return a[_ri(0,a.length-1)]; }
function _kf(x){ return String(x).replace('.','{,}'); }
function _round(n,d){ var p=Math.pow(10,d); return Math.round(n*p)/p; }
var PI=3.14;
function svgWrap(w,h,inner){ return '<div class="m6-fig" style="max-width:'+w+'px;margin:10px auto"><svg viewBox="0 0 '+w+' '+h+'" style="width:100%;height:auto;display:block;background:var(--card,#fff);border-radius:10px;border:1px solid var(--border)">'+inner+'</svg></div>'; }
/* тела */
var SOLIDS={
cube:'<polygon points="30,45 85,45 85,100 30,100" fill="rgba(217,119,6,.12)" stroke="#d97706" stroke-width="2"/><polygon points="30,45 50,25 105,25 85,45" fill="rgba(217,119,6,.20)" stroke="#d97706" stroke-width="2"/><polygon points="85,45 105,25 105,80 85,100" fill="rgba(217,119,6,.06)" stroke="#d97706" stroke-width="2"/>',
prism:'<polygon points="30,95 80,95 55,55" fill="rgba(217,119,6,.12)" stroke="#d97706" stroke-width="2"/><polygon points="60,95 110,95 85,55" fill="none" stroke="#d97706" stroke-width="2"/><line x1="30" y1="95" x2="60" y2="95" stroke="#d97706" stroke-width="2"/><line x1="80" y1="95" x2="110" y2="95" stroke="#d97706" stroke-width="2"/><line x1="55" y1="55" x2="85" y2="55" stroke="#d97706" stroke-width="2"/>',
pyramid:'<polygon points="30,95 95,95 110,75 45,75" fill="rgba(217,119,6,.10)" stroke="#d97706" stroke-width="2"/><line x1="30" y1="95" x2="68" y2="25" stroke="#d97706" stroke-width="2"/><line x1="95" y1="95" x2="68" y2="25" stroke="#d97706" stroke-width="2"/><line x1="110" y1="75" x2="68" y2="25" stroke="#d97706" stroke-width="2"/>',
cylinder:'<ellipse cx="68" cy="30" rx="35" ry="12" fill="rgba(217,119,6,.18)" stroke="#d97706" stroke-width="2"/><line x1="33" y1="30" x2="33" y2="95" stroke="#d97706" stroke-width="2"/><line x1="103" y1="30" x2="103" y2="95" stroke="#d97706" stroke-width="2"/><path d="M33,95 A35,12 0 0 0 103,95" fill="rgba(217,119,6,.08)" stroke="#d97706" stroke-width="2"/>',
cone:'<ellipse cx="68" cy="95" rx="38" ry="13" fill="rgba(217,119,6,.10)" stroke="#d97706" stroke-width="2"/><line x1="30" y1="95" x2="68" y2="20" stroke="#d97706" stroke-width="2"/><line x1="106" y1="95" x2="68" y2="20" stroke="#d97706" stroke-width="2"/>'
};
var SOLID_NAME={cube:'Куб',prism:'Треугольная призма',pyramid:'Пирамида',cylinder:'Цилиндр',cone:'Конус'};
var SOLID_FEV={cube:[6,12,8],prism:[5,9,6],pyramid:[5,8,5]};
var NETS={
cube:'<g fill="rgba(217,119,6,.12)" stroke="#d97706" stroke-width="1.8"><rect x="60" y="10" width="34" height="34"/><rect x="60" y="44" width="34" height="34"/><rect x="26" y="78" width="34" height="34"/><rect x="60" y="78" width="34" height="34"/><rect x="94" y="78" width="34" height="34"/><rect x="60" y="112" width="34" height="34"/></g>',
pyramid:'<g fill="rgba(217,119,6,.12)" stroke="#d97706" stroke-width="1.8"><rect x="55" y="60" width="44" height="44"/><polygon points="55,60 99,60 77,18"/><polygon points="99,60 99,104 141,82"/><polygon points="55,104 99,104 77,146"/><polygon points="55,60 55,104 13,82"/></g>',
cylinder:'<g fill="rgba(217,119,6,.12)" stroke="#d97706" stroke-width="1.8"><circle cx="40" cy="35" r="20"/><rect x="20" y="60" width="115" height="44"/><circle cx="40" cy="128" r="20"/></g>'
};
/* треугольники: классификация по координатам (svg, y вниз) */
function triClass(A,B,C){
function d(P,Q){ return Math.hypot(P.x-Q.x,P.y-Q.y); }
var c=d(A,B), a=d(B,C), b=d(C,A); /* a против A, b против B, c против C */
var arr=[a,b,c].slice().sort(function(x,y){return x-y;}); var eq=function(u,v){return Math.abs(u-v)<6;};
var side = (eq(a,b)&&eq(b,c))?'равносторонний':((eq(a,b)||eq(b,c)||eq(a,c))?'равнобедренный':'разносторонний');
var mx=arr[2], rest=arr[0]*arr[0]+arr[1]*arr[1]-arr[2]*arr[2];
var angle = Math.abs(rest)<60?'прямоугольный':(rest>0?'остроугольный':'тупоугольный');
/* вершина наибольшего угла = против наибольшей стороны */
var rv=null; if(angle==='прямоугольный'){ if(mx===a)rv=A; else if(mx===b)rv=B; else rv=C; }
/* равные стороны для штрихов */
var ticks=[];
if(side==='равносторонний'){ ticks=[[A,B,1],[B,C,1],[C,A,1]]; }
else if(side==='равнобедренный'){ if(eq(a,b)){ticks=[[B,C,2],[C,A,2]];} else if(eq(b,c)){ticks=[[C,A,2],[A,B,2]];} else {ticks=[[A,B,2],[B,C,2]];} }
return {side:side, angle:angle, ticks:ticks, rv:rv};
}
function drawTri(A,B,C,info,color){
color=color||'#d97706';
var s='<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+C.x+','+C.y+'" fill="rgba(217,119,6,.10)" stroke="'+color+'" stroke-width="2.5" stroke-linejoin="round"/>';
(info.ticks||[]).forEach(function(t){ var P=t[0],Q=t[1],n=t[2]; var mx=(P.x+Q.x)/2,my=(P.y+Q.y)/2,dx=Q.x-P.x,dy=Q.y-P.y,L=Math.hypot(dx,dy)||1,ux=dx/L,uy=dy/L,nx=-uy,ny=ux;
for(var k=0;k<n;k++){ var off=(k-(n-1)/2)*5,cx=mx+ux*off,cy=my+uy*off; s+='<line x1="'+(cx+nx*6)+'" y1="'+(cy+ny*6)+'" x2="'+(cx-nx*6)+'" y2="'+(cy-ny*6)+'" stroke="'+color+'" stroke-width="2"/>'; } });
if(info.rv){ var V=info.rv, o1=(V===A?B:A), o2=(V===C?B:C); var u1x=(o1.x-V.x),u1y=(o1.y-V.y),L1=Math.hypot(u1x,u1y)||1; u1x/=L1;u1y/=L1; var u2x=(o2.x-V.x),u2y=(o2.y-V.y),L2=Math.hypot(u2x,u2y)||1; u2x/=L2;u2y/=L2; var m=12; s+='<polyline points="'+(V.x+u1x*m)+','+(V.y+u1y*m)+' '+(V.x+u1x*m+u2x*m)+','+(V.y+u1y*m+u2y*m)+' '+(V.x+u2x*m)+','+(V.y+u2y*m)+'" fill="none" stroke="'+color+'" stroke-width="1.6"/>'; }
return s;
}
var TRIS=[
[{x:100,y:55},{x:45,y:150},{x:155,y:150}], /* равносторонний остроуг. */
[{x:100,y:30},{x:60,y:150},{x:140,y:150}], /* равнобедренный остроуг. */
[{x:50,y:50},{x:50,y:150},{x:150,y:150}], /* прямоуг. равнобедр. */
[{x:40,y:40},{x:40,y:150},{x:160,y:150}], /* прямоуг. разносторонний */
[{x:20,y:60},{x:60,y:150},{x:190,y:150}], /* тупоуг. разносторонний */
[{x:70,y:35},{x:30,y:150},{x:175,y:135}] /* разносторонний остроуг. */
];
/* ===================== § 1. ТЕЛА И РАЗВЁРТКИ ===================== */
function buildP1(){
var box=document.getElementById('p1-body'); var h='';
h+=makeCard('theory','Тела в пространстве','1.1',
'<p>Окружающие предметы имеют форму <b>геометрических тел</b>: коробка — это прямоугольный параллелепипед (частный случай — <b>куб</b>), палатка — <b>призма</b>, египетские сооружения — <b>пирамида</b>, банка — <b>цилиндр</b>, рожок мороженого — <b>конус</b>.</p>'
+'<p>У многогранников есть <b>грани</b> (плоские стороны), <b>рёбра</b> (линии стыка граней) и <b>вершины</b> (точки стыка рёбер).</p>'
+'<div style="display:flex;flex-wrap:wrap;gap:10px;justify-content:center">'
+['cube','prism','pyramid','cylinder','cone'].map(function(k){ return '<div style="text-align:center">'+svgWrap(140,120,SOLIDS[k])+'<div style="font-size:.8rem;color:var(--muted)">'+SOLID_NAME[k]+'</div></div>'; }).join('')+'</div>');
h+=makeCard('theory','Развёртка тела','1.2',
'<p><b>Развёртка</b> — это фигура на плоскости, из которой можно склеить тело. У куба развёртка состоит из 6 квадратов, у цилиндра — из прямоугольника и двух кругов.</p>'
+'<div style="display:flex;flex-wrap:wrap;gap:10px;justify-content:center">'+svgWrap(160,160,NETS.cube)+svgWrap(160,160,NETS.cylinder)+'</div>');
h+='<div class="wg" id="p1-iv1"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><div class="wg-title">Грани, рёбра, вершины</div></div>'
+'<div class="wg-help">Посмотри на многогранник и ответь, сколько у него граней, рёбер или вершин.</div>'
+'<div class="score-display"><span>Вопрос <b id="p1-i">1</b> / 6</span><span>Очки: <b id="p1-s">0</b> / 6</span></div>'
+'<div id="p1-fig"></div><div id="p1-q" class="qbox"></div>'
+'<div style="display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap"><input type="number" id="p1-a" class="tinp" style="width:80px;text-align:center"><button class="btn primary" id="p1-go">Проверить</button></div>'
+'<div class="feedback" id="p1-fb"></div></div>';
h+='<div class="wg" id="p1-iv2"><div class="wg-header"><span class="wg-badge">Интерактив 2</span><div class="wg-title">Какое тело из развёртки?</div></div>'
+'<div class="wg-help">Определи, какое тело сложится из показанной развёртки.</div>'
+'<div class="score-display"><span>Вопрос <b id="p1-di">1</b> / 4</span><span>Очки: <b id="p1-ds">0</b> / 4</span></div>'
+'<div id="p1-dfig"></div>'
+'<div id="p1-dopts" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap"></div>'
+'<div class="feedback" id="p1-dfb"></div></div>';
h+=secNav(null,'p2')+readBtn('p1');
box.innerHTML=h; renderMath(box);
(function(){
var i=0,score=0,cur=null, polys=['cube','prism','pyramid'], lbl=['граней','рёбер','вершин'];
function gen(){ var k=_pick(polys), j=_ri(0,2); cur={k:k, j:j, ans:SOLID_FEV[k][j]}; }
function show(){ if(i>=6){ document.getElementById('p1-fig').innerHTML=''; document.getElementById('p1-q').innerHTML='<b>Готово!</b> Результат: '+score+' / 6'; if(score>=5){addXp(15,'p1-iv1');bumpProgress('p1',30);}else if(score>=3){addXp(8,'p1-iv1');bumpProgress('p1',16);} return; }
gen(); document.getElementById('p1-i').textContent=i+1;
document.getElementById('p1-fig').innerHTML=svgWrap(140,120,SOLIDS[cur.k]);
document.getElementById('p1-q').innerHTML='Сколько <b>'+lbl[cur.j]+'</b> у тела «'+SOLID_NAME[cur.k]+'»?';
document.getElementById('p1-a').value=''; document.getElementById('p1-fb').style.display='none'; }
function go(){ if(i>=6)return; var fb=document.getElementById('p1-fb'), v=parseInt(document.getElementById('p1-a').value,10);
if(isNaN(v)){ feedback(fb,false,'Введи число.'); return; }
if(v===cur.ans){ score++; feedback(fb,true,'✓ Верно: '+cur.ans+'.'); } else feedback(fb,false,'✗ Нет. У «'+SOLID_NAME[cur.k]+'» '+cur.ans+' '+lbl[cur.j]+'.');
document.getElementById('p1-s').textContent=score; i++; setTimeout(show,1300); }
document.getElementById('p1-go').addEventListener('click',go);
document.getElementById('p1-a').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); show();
})();
(function(){
var i=0,score=0,cur=null, keys=['cube','pyramid','cylinder'];
function gen(){ cur=_pick(keys); }
function show(){ if(i>=4){ document.getElementById('p1-dfig').innerHTML=''; document.getElementById('p1-dopts').innerHTML=''; if(score>=3){addXp(15,'p1-iv2');bumpProgress('p1',30);}else if(score>=2){addXp(8,'p1-iv2');bumpProgress('p1',16);} return; }
gen(); document.getElementById('p1-di').textContent=i+1;
document.getElementById('p1-dfig').innerHTML=svgWrap(160,160,NETS[cur]);
var opts=['cube','pyramid','cylinder','cone']; for(var j=opts.length-1;j>0;j--){var k=_ri(0,j),t=opts[j];opts[j]=opts[k];opts[k]=t;}
document.getElementById('p1-dopts').innerHTML=opts.map(function(o){ return '<button class="btn primary" data-o="'+o+'">'+SOLID_NAME[o]+'</button>'; }).join('');
document.querySelectorAll('#p1-dopts [data-o]').forEach(function(b){ b.addEventListener('click',function(){ ans(b.getAttribute('data-o')); }); });
document.getElementById('p1-dfb').style.display='none'; }
function ans(o){ if(i>=4)return; var fb=document.getElementById('p1-dfb');
if(o===cur){ score++; feedback(fb,true,'✓ Верно — '+SOLID_NAME[cur]+'.'); } else feedback(fb,false,'✗ Нет. Это развёртка «'+SOLID_NAME[cur]+'».');
document.getElementById('p1-ds').textContent=score; i++; setTimeout(show,1300); }
show();
})();
}
/* ===================== § 2. ОКРУЖНОСТЬ. КРУГ ===================== */
function buildP2(){
var box=document.getElementById('p2-body'); var h='';
h+=makeCard('theory','Окружность и круг','2.1',
'<p><b>Окружность</b> — замкнутая линия, все точки которой одинаково удалены от центра. <b>Круг</b> — часть плоскости, ограниченная окружностью.</p>'
+'<p><b>Радиус</b> $r$ — расстояние от центра до окружности; <b>диаметр</b> $d=2r$.</p>');
h+=makeCard('rule','Длина окружности и площадь круга','2.2',
'<p>Длина окружности: $C = 2\\pi r = \\pi d$. Площадь круга: $S = \\pi r^2$.</p>'
+'<p>Число $\\pi \\approx 3{,}14$ — отношение длины окружности к диаметру (одинаково для любой окружности).</p>');
h+='<div class="wg" id="p2-iv1"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><div class="wg-title">Радиус, длина, площадь</div></div>'
+'<div class="wg-help">Двигай радиус — смотри, как меняются длина окружности и площадь круга ($\\pi=3{,}14$).</div>'
+'<div class="sliders"><label>Радиус $r$ = <b id="p2-rv">3</b><input type="range" id="p2-r" min="1" max="8" value="3"></label></div>'
+'<div id="p2-fig"></div><div id="p2-out" class="qbox"></div></div>';
h+='<div class="wg" id="p2-iv2"><div class="wg-header"><span class="wg-badge">Интерактив 2</span><div class="wg-title">Тренажёр: вычисли C или S</div></div>'
+'<div class="wg-help">Используй $\\pi=3{,}14$. Ответ — число.</div>'
+'<div class="score-display"><span>Задача <b id="p2-i">1</b> / 6</span><span>Очки: <b id="p2-s">0</b> / 6</span></div>'
+'<div id="p2-q" class="qbox"></div>'
+'<div style="display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap"><input type="text" id="p2-a" class="tinp" style="width:110px;text-align:center"><button class="btn primary" id="p2-go">Проверить</button></div>'
+'<div class="feedback" id="p2-fb"></div></div>';
h+=secNav('p1','p3')+readBtn('p2');
box.innerHTML=h; renderMath(box);
(function(){
var sl=document.getElementById('p2-r'), fig=document.getElementById('p2-fig'), out=document.getElementById('p2-out');
function render(){ var r=+sl.value; document.getElementById('p2-rv').textContent=r;
var rpx=10+r*11, cx=110, cy=85;
fig.innerHTML=svgWrap(220,170,'<circle cx="'+cx+'" cy="'+cy+'" r="'+rpx+'" fill="rgba(217,119,6,.10)" stroke="#d97706" stroke-width="2.5"/><line x1="'+cx+'" y1="'+cy+'" x2="'+(cx+rpx)+'" y2="'+cy+'" stroke="#b45309" stroke-width="2" stroke-dasharray="4 3"/><circle cx="'+cx+'" cy="'+cy+'" r="3" fill="#b45309"/><text x="'+(cx+rpx/2)+'" y="'+(cy-6)+'" font-size="12" font-family="JetBrains Mono,monospace" fill="#b45309">r='+r+'</text>');
out.innerHTML='<div style="font-size:1.05rem;color:var(--pri2);font-weight:700">$C = 2\\pi r = 2\\cdot 3{,}14\\cdot '+r+' = '+_kf(_round(2*PI*r,2))+'$</div>'
+'<div style="font-size:1.05rem;color:var(--pri2);font-weight:700;margin-top:4px">$S = \\pi r^2 = 3{,}14\\cdot '+r+'^2 = '+_kf(_round(PI*r*r,2))+'$</div>'; renderMath(out); }
sl.oninput=render; render();
})();
(function(){
var i=0,score=0,cur=null;
function gen(){ var r=_ri(2,10), what=_pick(['C','S']); cur={r:r, what:what, ans:what==='C'?_round(2*PI*r,2):_round(PI*r*r,2)}; }
function show(){ if(i>=6){ document.getElementById('p2-q').innerHTML='<b>Готово!</b> Результат: '+score+' / 6'; if(score>=5){addXp(15,'p2-iv2');bumpProgress('p2',30);}else if(score>=3){addXp(8,'p2-iv2');bumpProgress('p2',16);} return; }
gen(); document.getElementById('p2-i').textContent=i+1;
document.getElementById('p2-q').innerHTML=(cur.what==='C'?'Найди длину окружности $C$':'Найди площадь круга $S$')+' при $r='+cur.r+'$ ($\\pi=3{,}14$).'; renderMath(document.getElementById('p2-q'));
document.getElementById('p2-a').value=''; document.getElementById('p2-fb').style.display='none'; }
function go(){ if(i>=6)return; var fb=document.getElementById('p2-fb'), v=parseFloat(document.getElementById('p2-a').value.replace(',','.').trim());
if(isNaN(v)){ feedback(fb,false,'Введи число.'); return; }
if(Math.abs(v-cur.ans)<0.011){ score++; feedback(fb,true,'✓ Верно: $'+_kf(cur.ans)+'$.'); } else feedback(fb,false,'✗ Нет. Правильно: $'+_kf(cur.ans)+'$.');
document.getElementById('p2-s').textContent=score; i++; setTimeout(show,1300); }
document.getElementById('p2-go').addEventListener('click',go);
document.getElementById('p2-a').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); show();
})();
}
/* ===================== § 3. ВИДЫ ТРЕУГОЛЬНИКОВ ===================== */
function buildP3(){
var box=document.getElementById('p3-body'); var h='';
h+=makeCard('rule','По сторонам','3.1',
'<p><b>Разносторонний</b> — все стороны разные. <b>Равнобедренный</b> — две стороны равны. <b>Равносторонний</b> — все три стороны равны (равные стороны отмечают одинаковыми штрихами).</p>');
h+=makeCard('rule','По углам','3.2',
'<p><b>Остроугольный</b> — все углы острые ($<90°$). <b>Прямоугольный</b> — есть прямой угол ($=90°$). <b>Тупоугольный</b> — есть тупой угол ($>90°$).</p>');
h+='<div class="wg" id="p3-iv1"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><div class="wg-title">Вид по сторонам</div></div>'
+'<div class="wg-help">Определи вид треугольника по сторонам (штрихи отмечают равные стороны).</div>'
+'<div class="score-display"><span>Вопрос <b id="p3-i">1</b> / 5</span><span>Очки: <b id="p3-s">0</b> / 5</span></div>'
+'<div id="p3-fig"></div>'
+'<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap"><button class="btn primary" data-v="разносторонний">разносторонний</button><button class="btn primary" data-v="равнобедренный">равнобедренный</button><button class="btn primary" data-v="равносторонний">равносторонний</button></div>'
+'<div class="feedback" id="p3-fb"></div></div>';
h+='<div class="wg" id="p3-iv2"><div class="wg-header"><span class="wg-badge">Интерактив 2</span><div class="wg-title">Вид по углам</div></div>'
+'<div class="wg-help">Определи вид треугольника по его углам.</div>'
+'<div class="score-display"><span>Вопрос <b id="p3-ai">1</b> / 5</span><span>Очки: <b id="p3-as">0</b> / 5</span></div>'
+'<div id="p3-afig"></div>'
+'<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap"><button class="btn primary" data-a="остроугольный">остроугольный</button><button class="btn primary" data-a="прямоугольный">прямоугольный</button><button class="btn primary" data-a="тупоугольный">тупоугольный</button></div>'
+'<div class="feedback" id="p3-afb"></div></div>';
h+=secNav('p2','p4')+readBtn('p3');
box.innerHTML=h; renderMath(box);
(function(){
var i=0,score=0,cur=null;
function gen(){ var t=_pick(TRIS), info=triClass(t[0],t[1],t[2]); cur={t:t,info:info}; }
function show(){ if(i>=5){ document.getElementById('p3-fig').innerHTML='<b>Готово!</b> '+score+' / 5'; if(score>=4){addXp(15,'p3-iv1');bumpProgress('p3',30);}else if(score>=2){addXp(8,'p3-iv1');bumpProgress('p3',16);} return; }
gen(); document.getElementById('p3-i').textContent=i+1;
document.getElementById('p3-fig').innerHTML=svgWrap(210,180,drawTri(cur.t[0],cur.t[1],cur.t[2],cur.info));
document.getElementById('p3-fb').style.display='none'; }
function ans(v){ if(i>=5)return; var fb=document.getElementById('p3-fb');
if(v===cur.info.side){ score++; feedback(fb,true,'✓ Верно — '+cur.info.side+'.'); } else feedback(fb,false,'✗ Нет. Это '+cur.info.side+' треугольник.');
document.getElementById('p3-s').textContent=score; i++; setTimeout(show,1300); }
document.querySelectorAll('#p3-iv1 [data-v]').forEach(function(b){ b.addEventListener('click',function(){ ans(b.getAttribute('data-v')); }); }); show();
})();
(function(){
var i=0,score=0,cur=null;
function gen(){ var t=_pick(TRIS), info=triClass(t[0],t[1],t[2]); cur={t:t,info:info}; }
function show(){ if(i>=5){ document.getElementById('p3-afig').innerHTML='<b>Готово!</b> '+score+' / 5'; if(score>=4){addXp(15,'p3-iv2');bumpProgress('p3',30);}else if(score>=2){addXp(8,'p3-iv2');bumpProgress('p3',16);} return; }
gen(); document.getElementById('p3-ai').textContent=i+1;
document.getElementById('p3-afig').innerHTML=svgWrap(210,180,drawTri(cur.t[0],cur.t[1],cur.t[2],{ticks:[],rv:cur.info.rv}));
document.getElementById('p3-afb').style.display='none'; }
function ans(a){ if(i>=5)return; var fb=document.getElementById('p3-afb');
if(a===cur.info.angle){ score++; feedback(fb,true,'✓ Верно — '+cur.info.angle+'.'); } else feedback(fb,false,'✗ Нет. Это '+cur.info.angle+' треугольник.');
document.getElementById('p3-as').textContent=score; i++; setTimeout(show,1300); }
document.querySelectorAll('#p3-iv2 [data-a]').forEach(function(b){ b.addEventListener('click',function(){ ans(b.getAttribute('data-a')); }); }); show();
})();
}
/* ===================== § 4. СИММЕТРИЯ ОТНОСИТЕЛЬНО ТОЧКИ ===================== */
function buildP4(){
var box=document.getElementById('p4-body'); var h='';
h+=makeCard('theory','Центральная симметрия','4.1',
'<p>Точки $A$ и $A\'$ <b>симметричны относительно точки</b> $O$, если $O$ — <b>середина</b> отрезка $AA\'$. Точка $O$ — <b>центр симметрии</b>.</p>'
+'<p>Координаты образа: если $A(x;\\,y)$ и центр $O(a;\\,b)$, то $A\'(2a-x;\\;2b-y)$. Относительно начала координат: $A(x;y)\\to A\'(-x;-y)$.</p>');
h+='<div class="wg" id="p4-iv1"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><div class="wg-title">Построй образ точки</div></div>'
+'<div class="wg-help">Найди координаты точки $A\'$, симметричной $A$ относительно центра $O$.</div>'
+'<div class="score-display"><span>Задача <b id="p4-i">1</b> / 5</span><span>Очки: <b id="p4-s">0</b> / 5</span></div>'
+'<div id="p4-fig"></div><div id="p4-q" class="qbox"></div>'
+'<div style="display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap">$x\'=$ <input type="number" id="p4-x" class="tinp" style="width:70px;text-align:center"> $y\'=$ <input type="number" id="p4-y" class="tinp" style="width:70px;text-align:center"><button class="btn primary" id="p4-go">Проверить</button></div>'
+'<div class="feedback" id="p4-fb"></div></div>';
h+=secNav('p3','p5')+readBtn('p4');
box.innerHTML=h; renderMath(box);
(function(){
var i=0,score=0,cur=null;
function gen(){ var ax=_ri(-4,4),ay=_ri(-4,4),ox=_ri(-2,2),oy=_ri(-2,2); cur={ax:ax,ay:ay,ox:ox,oy:oy,rx:2*ox-ax,ry:2*oy-ay}; }
function show(){ if(i>=5){ document.getElementById('p4-fig').innerHTML=''; document.getElementById('p4-q').innerHTML='<b>Готово!</b> '+score+' / 5'; if(score>=4){addXp(15,'p4-iv1');bumpProgress('p4',35);}else if(score>=2){addXp(8,'p4-iv1');bumpProgress('p4',18);} return; }
gen(); document.getElementById('p4-i').textContent=i+1;
document.getElementById('p4-fig').innerHTML=Math6.plane({size:320,points:[{x:cur.ax,y:cur.ay,label:'A',color:'#d97706'},{x:cur.ox,y:cur.oy,label:'O',color:'#0891b2'}]});
document.getElementById('p4-q').innerHTML='$A('+cur.ax+';\\,'+cur.ay+')$, центр $O('+cur.ox+';\\,'+cur.oy+')$. Найди $A\''+'$.'; renderMath(document.getElementById('p4-q'));
document.getElementById('p4-x').value=''; document.getElementById('p4-y').value=''; document.getElementById('p4-fb').style.display='none'; }
function go(){ if(i>=5)return; var fb=document.getElementById('p4-fb'), x=parseInt(document.getElementById('p4-x').value,10), y=parseInt(document.getElementById('p4-y').value,10);
if(isNaN(x)||isNaN(y)){ feedback(fb,false,'Введи обе координаты.'); return; }
if(x===cur.rx&&y===cur.ry){ score++; feedback(fb,true,'✓ Верно: $A\'('+cur.rx+';\\,'+cur.ry+')$.'); } else feedback(fb,false,'✗ Нет. $A\'(2\\cdot'+cur.ox+'-'+cur.ax+';\\,\\ldots)=('+cur.rx+';\\,'+cur.ry+')$.');
document.getElementById('p4-s').textContent=score; i++; setTimeout(show,1500); }
document.getElementById('p4-go').addEventListener('click',go);
document.getElementById('p4-y').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); show();
})();
}
/* ===================== § 5. СИММЕТРИЯ ОТНОСИТЕЛЬНО ПРЯМОЙ ===================== */
function buildP5(){
var box=document.getElementById('p5-body'); var h='';
h+=makeCard('theory','Осевая симметрия','5.1',
'<p>Точки $A$ и $A\'$ <b>симметричны относительно прямой</b> (оси), если эта прямая — <b>серединный перпендикуляр</b> отрезка $AA\'$: она проходит через середину $AA\'$ под прямым углом.</p>'
+'<p>Относительно оси $Oy$: $A(x;\\,y)\\to A\'(-x;\\,y)$. Относительно оси $Ox$: $A(x;\\,y)\\to A\'(x;\\,-y)$.</p>');
h+='<div class="wg" id="p5-iv1"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><div class="wg-title">Зеркало относительно оси</div></div>'
+'<div class="wg-help">Найди координаты точки, симметричной $A$ относительно указанной оси.</div>'
+'<div class="score-display"><span>Задача <b id="p5-i">1</b> / 6</span><span>Очки: <b id="p5-s">0</b> / 6</span></div>'
+'<div id="p5-fig"></div><div id="p5-q" class="qbox"></div>'
+'<div style="display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap">$x\'=$ <input type="number" id="p5-x" class="tinp" style="width:70px;text-align:center"> $y\'=$ <input type="number" id="p5-y" class="tinp" style="width:70px;text-align:center"><button class="btn primary" id="p5-go">Проверить</button></div>'
+'<div class="feedback" id="p5-fb"></div></div>';
h+=secNav('p4','final')+readBtn('p5');
box.innerHTML=h; renderMath(box);
(function(){
var i=0,score=0,cur=null;
function gen(){ var ax=_pick([-4,-3,-2,-1,1,2,3,4]),ay=_pick([-4,-3,-2,-1,1,2,3,4]),axis=_pick(['Oy','Ox']); cur={ax:ax,ay:ay,axis:axis,rx:axis==='Oy'?-ax:ax,ry:axis==='Oy'?ay:-ay}; }
function show(){ if(i>=6){ document.getElementById('p5-fig').innerHTML=''; document.getElementById('p5-q').innerHTML='<b>Готово!</b> '+score+' / 6'; if(score>=5){addXp(15,'p5-iv1');bumpProgress('p5',35);}else if(score>=3){addXp(8,'p5-iv1');bumpProgress('p5',18);} return; }
gen(); document.getElementById('p5-i').textContent=i+1;
document.getElementById('p5-fig').innerHTML=Math6.plane({size:320,points:[{x:cur.ax,y:cur.ay,label:'A',color:'#d97706'}]});
document.getElementById('p5-q').innerHTML='$A('+cur.ax+';\\,'+cur.ay+')$. Найди образ при симметрии относительно оси <b>$'+cur.axis+'$</b>.'; renderMath(document.getElementById('p5-q'));
document.getElementById('p5-x').value=''; document.getElementById('p5-y').value=''; document.getElementById('p5-fb').style.display='none'; }
function go(){ if(i>=6)return; var fb=document.getElementById('p5-fb'), x=parseInt(document.getElementById('p5-x').value,10), y=parseInt(document.getElementById('p5-y').value,10);
if(isNaN(x)||isNaN(y)){ feedback(fb,false,'Введи обе координаты.'); return; }
if(x===cur.rx&&y===cur.ry){ score++; feedback(fb,true,'✓ Верно: $A\'('+cur.rx+';\\,'+cur.ry+')$.'); } else feedback(fb,false,'✗ Нет. Относительно $'+cur.axis+'$: $A\'('+cur.rx+';\\,'+cur.ry+')$.');
document.getElementById('p5-s').textContent=score; i++; setTimeout(show,1500); }
document.getElementById('p5-go').addEventListener('click',go);
document.getElementById('p5-y').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); show();
})();
}
/* ===================== ФИНАЛ ГЛАВЫ — БОССЫ ===================== */
function buildFinal(){
var box=document.getElementById('final-body'); var h='';
h+=makeCard('theory','Финал главы 6','★','<p>Пять боссов проверят тела, окружность и круг, треугольники и симметрию. Победи всех!</p>');
h+='<div class="wg" id="fin"><div class="wg-header"><span class="wg-badge">Боссы</span><div class="wg-title">Сразись с главой 6</div></div>'
+'<div class="hp-boss"><div class="hp-boss-fill" id="fin-hp" style="width:100%"></div></div>'
+'<div class="score-display"><span>Босс <b id="fin-i">1</b> / 5</span><span>Побеждено: <b id="fin-s">0</b> / 5</span></div>'
+'<div id="fin-name" style="font-weight:800;color:#b91c1c;text-align:center;margin-bottom:6px"></div>'
+'<div id="fin-fig"></div><div id="fin-q" class="boss-q"></div>'
+'<div style="display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap"><input type="text" id="fin-a" class="tinp" style="width:140px;text-align:center" placeholder="ответ"><button class="btn primary" id="fin-go">Удар!</button></div>'
+'<div class="feedback" id="fin-fb"></div></div>';
h+=secNav('p5',null)+readBtn('final','Завершить главу 6 (+10 XP)');
box.innerHTML=h; renderMath(box);
(function(){
var bosses=[
function(){ var k=_pick(['cube','prism','pyramid']), j=_ri(0,2), lbl=['граней','рёбер','вершин']; return {name:'Хранитель Тел', fig:svgWrap(140,120,SOLIDS[k]), q:'Сколько <b>'+lbl[j]+'</b> у тела «'+SOLID_NAME[k]+'»?', ans:SOLID_FEV[k][j]}; },
function(){ var r=_ri(2,8); return {name:'Окружность', fig:'', q:'Найди длину окружности $C=2\\pi r$ при $r='+r+'$ ($\\pi=3{,}14$).', ans:_round(2*PI*r,2)}; },
function(){ var r=_ri(2,7); return {name:'Площадь Круга', fig:'', q:'Найди площадь круга $S=\\pi r^2$ при $r='+r+'$ ($\\pi=3{,}14$).', ans:_round(PI*r*r,2)}; },
function(){ var ax=_ri(-4,4),ay=_ri(-4,4); return {name:'Центр Симметрии', fig:Math6.plane({size:280,points:[{x:ax,y:ay,label:'A',color:'#d97706'}]}), q:'Точка $A('+ax+';\\,'+ay+')$. Найди <b>абсциссу</b> точки, симметричной $A$ относительно начала координат.', ans:-ax}; },
function(){ var ax=_pick([-4,-3,-2,-1,1,2,3,4]),ay=_ri(-4,4); return {name:'Зеркало', fig:'', q:'Точка $A('+ax+';\\,'+ay+')$. Найди <b>абсциссу</b> образа при симметрии относительно оси $Oy$.', ans:-ax}; }
];
var i=0,score=0,cur=null,done=false;
function show(){ if(i>=5){ done=true; document.getElementById('fin-name').textContent=''; document.getElementById('fin-fig').innerHTML=''; document.getElementById('fin-q').innerHTML=(score>=4?'<b>Победа!</b> Глава 6 пройдена. ':'<b>Бой окончен.</b> ')+'Побеждено боссов: '+score+' / 5.'; document.getElementById('fin-hp').style.width=(score>=4?0:40)+'%';
if(score>=4){ addXp(40,'final'); bumpProgress('final',100); if(window.confetti)try{confetti();}catch(e){} } else bumpProgress('final',60); return; }
cur=bosses[i](); document.getElementById('fin-i').textContent=i+1; document.getElementById('fin-s').textContent=score;
document.getElementById('fin-name').textContent='Босс '+(i+1)+': '+cur.name;
document.getElementById('fin-hp').style.width=(100-i*20)+'%';
document.getElementById('fin-fig').innerHTML=cur.fig||'';
document.getElementById('fin-q').innerHTML=cur.q; renderMath(document.getElementById('fin-q'));
document.getElementById('fin-a').value=''; document.getElementById('fin-fb').style.display='none'; }
function go(){ if(done||i>=5)return; var fb=document.getElementById('fin-fb'), v=parseFloat(document.getElementById('fin-a').value.replace(',','.').trim());
if(isNaN(v)){ feedback(fb,false,'Введи число.'); return; }
if(Math.abs(v-cur.ans)<0.011){ score++; feedback(fb,true,'✓ Босс повержен! Ответ '+_kf(cur.ans)+'.'); } else feedback(fb,false,'✗ Босс устоял. Верно: '+_kf(cur.ans)+'.');
document.getElementById('fin-s').textContent=score; i++; setTimeout(show,1400); }
document.getElementById('fin-go').addEventListener('click',go);
document.getElementById('fin-a').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); show();
})();
}
/* ===================== ДАННЫЕ ===================== */
var SIDEBARS = {
p1:{ title:'Шпаргалка § 1', rows:[
['Грань','плоская сторона тела'],
['Ребро','линия стыка граней'],
['Вершина','точка стыка рёбер'],
['Куб','6 граней, 12 рёбер, 8 вершин'],
['Развёртка','фигура, из которой склеивают тело'] ]},
p2:{ title:'Шпаргалка § 2', rows:[
['Радиус','$r$ — центр→окружность'],
['Диаметр','$d=2r$'],
['Длина окружности','$C=2\\pi r=\\pi d$'],
['Площадь круга','$S=\\pi r^2$'],
['$\\pi$','$\\approx 3{,}14$'] ]},
p3:{ title:'Шпаргалка § 3', rows:[
['По сторонам','разносторонний / равнобедренный / равносторонний'],
['По углам','остро- / прямо- / тупоугольный'],
['Прямоугольный','есть угол $90°$'],
['Тупоугольный','есть угол $>90°$'] ]},
p4:{ title:'Шпаргалка § 4', rows:[
['Центр. симметрия','$O$ — середина $AA\'$'],
['Образ','$A\'(2a-x;\\,2b-y)$'],
['Относ. нуля','$A(x;y)\\to A\'(-x;-y)$'] ]},
p5:{ title:'Шпаргалка § 5', rows:[
['Осевая симметрия','ось — серединный перпендикуляр $AA\'$'],
['Относ. $Oy$','$(x;y)\\to(-x;y)$'],
['Относ. $Ox$','$(x;y)\\to(x;-y)$'] ]},
final:{ title:'Финал главы 6', rows:[
['5 боссов','тела, окружность, круг, симметрия'],
['Победа','4 из 5 и больше'],
['Награда','+40 XP и достижение «Глава 6 пройдена»'] ]}
};
var TIPS = [
{ sec:'p1', html:'Грани — это «стенки», рёбра — «края», вершины — «уголки». У куба: 6 стенок, 12 краёв, 8 уголков.' },
{ sec:'p2', html:'Сначала реши, что ищешь: длину ($C=2\\pi r$) или площадь ($S=\\pi r^2$). Не путай $r$ и $d=2r$.' },
{ sec:'p3', html:'Вид по сторонам и вид по углам — разные классификации. Равнобедренный может быть и остро-, и прямо-, и тупоугольным.' },
{ sec:'p4', html:'Центр симметрии — это середина. Координаты образа: удвой координаты центра и вычти координаты точки: $2a-x$.' },
{ sec:'p5', html:'Относительно оси $Oy$ меняется знак $x$; относительно оси $Ox$ — знак $y$. Вторая координата остаётся прежней.' }
];
var GLOSSARY = [
{ term:'грань', def:'Плоская сторона многогранника.', sec:'p1', aliases:['грань','грани','граней','гранью'] },
{ term:'ребро', def:'Линия, по которой пересекаются две грани.', sec:'p1', aliases:['ребро','ребра','рёбра','рёбер'] },
{ term:'вершина', def:'Точка, в которой сходятся рёбра.', sec:'p1', aliases:['вершина','вершины','вершин','вершиной'] },
{ term:'развёртка', def:'Фигура на плоскости, из которой можно склеить тело.', sec:'p1', aliases:['развёртка','развёртки','развёрток','развёртку'] },
{ term:'окружность', def:'Замкнутая линия, все точки которой равноудалены от центра.', sec:'p2', aliases:['окружность','окружности','окружностью'] },
{ term:'круг', def:'Часть плоскости, ограниченная окружностью.', sec:'p2', aliases:['круг','круга','круге','кругом'] },
{ term:'радиус', def:'Расстояние от центра до окружности; $d=2r$.', sec:'p2', aliases:['радиус','радиуса','радиусе','радиусом'] },
{ term:'диаметр', def:'Отрезок через центр; $d=2r$.', sec:'p2', aliases:['диаметр','диаметра','диаметре','диаметром'] },
{ term:'центральная симметрия', def:'Симметрия относительно точки $O$ — середины $AA\'$.', sec:'p4', aliases:['центральная симметрия','центральной симметрии','центр симметрии','центра симметрии'] },
{ term:'осевая симметрия', def:'Симметрия относительно прямой — серединного перпендикуляра $AA\'$.', sec:'p5', aliases:['осевая симметрия','осевой симметрии','ось симметрии','оси симметрии'] }
];
var BUILDERS = { p1:buildP1, p2:buildP2, p3:buildP3, p4:buildP4, p5:buildP5, final:buildFinal };
Object.assign(window.M6, { sidebars:SIDEBARS, tips:TIPS, glossary:GLOSSARY, builders:BUILDERS });
</script> </script>
</body> </body>