feat(geom10 W2): Раздел 1 §3 Сечения + Финал R1 (4 босса + ачивка)
§3 Построения сечений:
- Hero: куб с шестиугольным сечением через M, N, P (4-шаговая анимация: точки → 2 ребра → 6 точек → заливка)
- 3 типа сечений куба: треугольник / прямоугольник / правильный шестиугольник
- Метод следов: куб с M, N, K и следом плоскости сечения на основании
- 4 теоретические карточки (определение, метод следов, параллельные сечения, max сторон)
- 3 тренажёра: тип многоугольника (6), max сторон (5), метод следов (5)
- Босс §3: 5 этапов, +70 XP
Финал раздела 1 (4 босса):
- Босс 1 Элементы тел (4 этапа, +35 XP)
- Босс 2 Аксиомы (4 этапа, +35 XP)
- Босс 3 Сечения (4 этапа, +35 XP)
- Босс 4 Сборная (5 этапов, +45 XP)
- Celebration: ачивка stereo10_r1_master + 100 XP бонус
- Прогресс хранится в STATE.bosses{f1..f4} + geometry10_achievements в localStorage
This commit is contained in:
@@ -195,8 +195,8 @@ main{max-width:1100px;margin:0 auto;padding:32px 24px 80px}
|
||||
<div class="sec-nav-inner">
|
||||
<a class="sec-tab active" data-tab="1" href="#para-1"><span class="dot"></span>§1 Фигуры</a>
|
||||
<a class="sec-tab" data-tab="2" href="#para-2"><span class="dot"></span>§2 Аксиомы</a>
|
||||
<a class="sec-tab locked" data-tab="3" href="#para-3"><span class="dot"></span>§3 Сечения</a>
|
||||
<a class="sec-tab locked" data-tab="final" href="#para-final"><span class="dot"></span>Финал</a>
|
||||
<a class="sec-tab" data-tab="3" href="#para-3"><span class="dot"></span>§3 Сечения</a>
|
||||
<a class="sec-tab" data-tab="final" href="#para-final"><span class="dot"></span>Финал</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -503,10 +503,124 @@ main{max-width:1100px;margin:0 auto;padding:32px 24px 80px}
|
||||
<div class="para-h-sub">Метод следов · сечения куба, призмы, пирамиды</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stub-card">
|
||||
<b>В разработке (Волна W2)</b>
|
||||
Параграф появится в следующей волне: сложная анимированная визуализация построения сечений многогранников.
|
||||
<small>Сейчас сосредоточься на §1 и §2 — они дают контекст для §3.</small>
|
||||
|
||||
<div class="viz">
|
||||
<div class="viz-title"><span class="badge">МЕТОД 3 ТОЧЕК</span> Сечение куба плоскостью через $M$, $N$, $P$ — правильный шестиугольник</div>
|
||||
<div id="viz3-hero" style="text-align:center"></div>
|
||||
<div class="rot-row" style="margin-top:14px">
|
||||
<button id="viz3-step-btn" class="mark-btn" style="padding:8px 18px;font-size:.84rem">Шаг построения →</button>
|
||||
<span id="viz3-step-lab" style="display:inline-flex;align-items:center;font-family:JetBrains Mono,monospace;color:var(--pri-d);font-weight:700">Шаг 1 / 4</span>
|
||||
</div>
|
||||
<div class="viz-cap" id="viz3-cap">На рёбрах $AB$, $BC$, $CC_1$ куба отмечены середины $M$, $N$, $P$. Нажми «Шаг построения», чтобы увидеть, как через них проводится плоскость и какая фигура получится.</div>
|
||||
</div>
|
||||
|
||||
<div class="viz">
|
||||
<div class="viz-title"><span class="badge">ТИПЫ СЕЧЕНИЙ КУБА</span> От треугольника до шестиугольника</div>
|
||||
<div class="viz-row">
|
||||
<div class="viz-cell"><div id="viz3-tri"></div><div class="viz-cell-label">Треугольник</div></div>
|
||||
<div class="viz-cell"><div id="viz3-quad"></div><div class="viz-cell-label">Прямоугольник</div></div>
|
||||
<div class="viz-cell"><div id="viz3-hex"></div><div class="viz-cell-label">Шестиугольник (max)</div></div>
|
||||
</div>
|
||||
<div class="viz-cap">Куб «теряет» одну грань при пересечении плоскостью — поэтому сечение имеет <b>от 3 до 6 сторон</b>. Тетраэдр (4 грани) — максимум 4-угольник. Призма с $n$-угольным основанием — максимум $(n+2)$-угольник.</div>
|
||||
</div>
|
||||
|
||||
<div class="viz">
|
||||
<div class="viz-title"><span class="badge">МЕТОД СЛЕДОВ</span> Как строится сечение, выходящее за пределы видимых граней</div>
|
||||
<div id="viz3-trace" style="text-align:center"></div>
|
||||
<div class="viz-cap">Пусть в кубе отмечены $M$, $N$ на верхних рёбрах и $K$ на боковом ребре. Тогда: 1) находим <span style="color:#dc2626;font-weight:800">след</span> плоскости сечения на основании (продолжая $MN$ и проводя прямую через $K$ параллельно ребру); 2) от следа достраиваем сечение, пересекая остальные рёбра.</div>
|
||||
</div>
|
||||
|
||||
<div class="theory">
|
||||
<div class="t-card">
|
||||
<span class="t-tag">3.1</span>
|
||||
<div class="t-title">Сечение многогранника</div>
|
||||
<div class="t-body">
|
||||
<p><b>Сечение</b> многогранника плоскостью $\sigma$ — это многоугольник, образованный пересечением плоскости со всеми гранями многогранника.</p>
|
||||
<p>Его стороны — отрезки пересечения $\sigma$ с гранями; его вершины — точки, где $\sigma$ пересекает рёбра.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="t-card">
|
||||
<span class="t-tag">3.2</span>
|
||||
<div class="t-title">Метод следов</div>
|
||||
<div class="t-body">
|
||||
<p><b>Следом</b> называется линия пересечения плоскости сечения с одной из граней (обычно с плоскостью основания).</p>
|
||||
<p>Зная след в плоскости основания и одну точку выше, можно построить пересечения с остальными гранями, продолжая прямые и применяя аксиому A2.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="t-card">
|
||||
<span class="t-tag">3.3</span>
|
||||
<div class="t-title">Параллельные сечения</div>
|
||||
<div class="t-body">
|
||||
<p>Если плоскость сечения <b>параллельна основанию</b> пирамиды, сечение — многоугольник, <b>подобный основанию</b> (с коэффициентом подобия, зависящим от высоты).</p>
|
||||
<p>В призме параллельное основанию сечение даёт многоугольник, <b>равный основанию</b>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="t-card">
|
||||
<span class="t-tag">3.4</span>
|
||||
<div class="t-title">Максимальное число сторон</div>
|
||||
<div class="t-body">
|
||||
<p>Плоскость может пересечь каждую грань не более чем по одному отрезку. Поэтому число сторон сечения не превосходит число граней многогранника.</p>
|
||||
<ul>
|
||||
<li>Куб (6 граней) → max <b>6 сторон</b> (правильный шестиугольник через 6 средин рёбер).</li>
|
||||
<li>Тетраэдр (4 грани) → max <b>4 стороны</b>.</li>
|
||||
<li>$n$-угольная призма ($n{+}2$ граней) → max $n{+}2$ сторон.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inter" data-inter="i3-type">
|
||||
<div class="inter-h">
|
||||
<div class="inter-icon">1</div>
|
||||
<div class="inter-title">Какой многоугольник получится в сечении?</div>
|
||||
<div class="inter-progress" id="i3-type-prog">0 / 6</div>
|
||||
</div>
|
||||
<div class="quiz">
|
||||
<div class="quiz-q" id="i3-type-q"></div>
|
||||
<div class="quiz-opts" id="i3-type-opts"></div>
|
||||
</div>
|
||||
<div class="quiz-feedback" id="i3-type-fb"></div>
|
||||
</div>
|
||||
|
||||
<div class="inter" data-inter="i3-max">
|
||||
<div class="inter-h">
|
||||
<div class="inter-icon">2</div>
|
||||
<div class="inter-title">Максимальное число сторон сечения</div>
|
||||
<div class="inter-progress" id="i3-max-prog">0 / 5</div>
|
||||
</div>
|
||||
<div class="quiz">
|
||||
<div class="quiz-q" id="i3-max-q"></div>
|
||||
<div class="quiz-input">
|
||||
<input id="i3-max-in" type="text" inputmode="numeric" placeholder="?">
|
||||
<button id="i3-max-go">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="quiz-feedback" id="i3-max-fb"></div>
|
||||
</div>
|
||||
|
||||
<div class="inter" data-inter="i3-trace">
|
||||
<div class="inter-h">
|
||||
<div class="inter-icon">3</div>
|
||||
<div class="inter-title">Метод следов: верно или нет?</div>
|
||||
<div class="inter-progress" id="i3-trace-prog">0 / 5</div>
|
||||
</div>
|
||||
<div class="quiz">
|
||||
<div class="quiz-q" id="i3-trace-q"></div>
|
||||
<div class="quiz-opts" id="i3-trace-opts"></div>
|
||||
</div>
|
||||
<div class="quiz-feedback" id="i3-trace-fb"></div>
|
||||
</div>
|
||||
|
||||
<div class="boss" id="boss-3"></div>
|
||||
|
||||
<div class="para-actions">
|
||||
<button class="mark-btn" id="mark-3">
|
||||
<svg class="ic" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
<span>Отметить §3 как изученный</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -515,14 +629,21 @@ main{max-width:1100px;margin:0 auto;padding:32px 24px 80px}
|
||||
<div class="para-num">★</div>
|
||||
<div class="para-h">
|
||||
<h2>Финал раздела 1</h2>
|
||||
<div class="para-h-sub">4 интегральных босса · ачивка «Введение в стереометрию пройдено!»</div>
|
||||
<div class="para-h-sub">4 интегральных босса · ачивка «Введение в стереометрию пройдено»</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stub-card">
|
||||
<b>Откроется после §3 (Волна W2)</b>
|
||||
Финал содержит 4 босса (элементы тел, аксиомы, сечения, сборная задача) и спецачивку.
|
||||
<small>До этого момента — побеждай боссов §1 и §2, чтобы заработать XP.</small>
|
||||
|
||||
<div class="viz" style="background:linear-gradient(135deg,#dbeafe,#eff6ff);border-color:var(--pri-l)">
|
||||
<div class="viz-title" style="color:var(--pri-d)"><span class="badge">ФИНАЛЬНОЕ ИСПЫТАНИЕ</span> Победи 4 боссов подряд</div>
|
||||
<div class="viz-cap" style="color:var(--text-2);margin-top:0">Каждый босс — на одну тему: <b>элементы тел</b>, <b>аксиомы</b>, <b>сечения</b>, <b>сборная задача</b>. После победы над всеми 4 — получишь ачивку и +100 XP бонусом. Состояние сохраняется автоматически.</div>
|
||||
</div>
|
||||
|
||||
<div class="boss" id="boss-f1"></div>
|
||||
<div class="boss" id="boss-f2"></div>
|
||||
<div class="boss" id="boss-f3"></div>
|
||||
<div class="boss" id="boss-f4"></div>
|
||||
|
||||
<div id="celebration" style="display:none"></div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
@@ -601,9 +722,14 @@ function refreshMarkBtn(n){
|
||||
function refreshTabs(){
|
||||
document.querySelectorAll('.sec-tab').forEach(function(t){
|
||||
var n = t.getAttribute('data-tab');
|
||||
if (n === '1' || n === '2'){
|
||||
if (n === '1' || n === '2' || n === '3'){
|
||||
if (STATE.read.indexOf(parseInt(n,10)) >= 0) t.classList.add('read');
|
||||
else t.classList.remove('read');
|
||||
} else if (n === 'final'){
|
||||
var allBeat = ['f1','f2','f3','f4'].every(function(k){
|
||||
return STATE.bosses && STATE.bosses[k] && STATE.bosses[k].defeated;
|
||||
});
|
||||
if (allBeat) t.classList.add('read');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -758,6 +884,160 @@ function buildAxiomVizes(){
|
||||
})();
|
||||
}
|
||||
|
||||
/* ===== §3 SVGs ===== */
|
||||
var SECTION_STEP = 0;
|
||||
function buildSectionHero(){
|
||||
if (!window.STEREO3D) return setTimeout(buildSectionHero, 80);
|
||||
var S = window.STEREO3D;
|
||||
var sc = new S.Scene(440, 360, {view:'CABINET', scale:60});
|
||||
sc.addCube({center:[0,0,0], size:2.0, labels:true, color:'#dbeafe', opacity:0.18});
|
||||
|
||||
// 3 заданные точки (шаг ≥ 1)
|
||||
if (SECTION_STEP >= 1){
|
||||
sc.addVertex([0,-1,-1], 'M', {dx:-14, dy:14, color:'#dc2626'});
|
||||
sc.addVertex([1, 0,-1], 'N', {dx:10, dy:14, color:'#dc2626'});
|
||||
sc.addVertex([1, 1, 0], 'P', {dx:14, dy:-2, color:'#dc2626'});
|
||||
}
|
||||
|
||||
// Шаг 2: добавляем рёбра первой грани и продолжение
|
||||
if (SECTION_STEP >= 2){
|
||||
sc.addEdge([0,-1,-1],[1, 0,-1], {stroke:'#dc2626', width:2.6}); // M-N на нижней грани
|
||||
sc.addEdge([1, 0,-1],[1, 1, 0], {stroke:'#dc2626', width:2.6}); // N-P на правой грани
|
||||
}
|
||||
|
||||
// Шаг 3: остальные стороны (полный шестиугольник)
|
||||
if (SECTION_STEP >= 3){
|
||||
// P-R на задней грани, R на C1D1: R=(0,1,1)
|
||||
sc.addEdge([1, 1, 0],[0, 1, 1], {stroke:'#dc2626', width:2.6});
|
||||
// R-S на верхней грани, S на D1A1: S=(-1,0,1)
|
||||
sc.addEdge([0, 1, 1],[-1, 0, 1], {stroke:'#dc2626', width:2.6});
|
||||
// S-Q на левой грани, Q на AA1: Q=(-1,-1,0)
|
||||
sc.addEdge([-1, 0, 1],[-1,-1, 0], {stroke:'#dc2626', width:2.6});
|
||||
// Q-M на передней грани
|
||||
sc.addEdge([-1,-1, 0],[ 0,-1,-1], {stroke:'#dc2626', width:2.6});
|
||||
sc.addVertex([0, 1, 1], 'R', {dx:-12, dy:-10, color:'#dc2626'});
|
||||
sc.addVertex([-1, 0, 1], 'S', {dx:-22, dy:-4, color:'#dc2626'});
|
||||
sc.addVertex([-1,-1, 0], 'Q', {dx:-22, dy:6, color:'#dc2626'});
|
||||
}
|
||||
|
||||
// Шаг 4: заливка многоугольника (правильный шестиугольник)
|
||||
if (SECTION_STEP >= 4){
|
||||
sc.addFace([[0,-1,-1],[1,0,-1],[1,1,0],[0,1,1],[-1,0,1],[-1,-1,0]],
|
||||
{fill:'#fca5a5', opacity:0.45, stroke:'#dc2626', strokeWidth:2});
|
||||
}
|
||||
|
||||
document.getElementById('viz3-hero').innerHTML = sc.render();
|
||||
var lab = document.getElementById('viz3-step-lab');
|
||||
if (lab) lab.textContent = 'Шаг ' + (SECTION_STEP + 1) + ' / 4';
|
||||
var cap = document.getElementById('viz3-cap');
|
||||
var capTexts = [
|
||||
'На рёбрах $AB$, $BC$, $CC_1$ куба отмечены середины $M$, $N$, $P$. Нажми «Шаг построения», чтобы увидеть, как через них проводится плоскость и какая фигура получится.',
|
||||
'Точки $M$, $N$, $P$ определяют плоскость (3 неколлинеарные точки). По аксиоме A2 — отрезки $MN$ и $NP$ лежат на гранях куба, попадая в плоскость сечения.',
|
||||
'Продолжая прямые в плоскостях соседних граней, находим ещё 3 точки пересечения с рёбрами: $R$ на $C_1D_1$, $S$ на $D_1A_1$, $Q$ на $AA_1$.',
|
||||
'Готовый многоугольник $MNPRSQ$ — <b>правильный шестиугольник</b>. Это классическое сечение куба плоскостью через 6 средин рёбер, лежащих на трёх парах противоположных граней.'
|
||||
];
|
||||
if (cap) cap.innerHTML = capTexts[SECTION_STEP];
|
||||
tryKatex(cap);
|
||||
}
|
||||
|
||||
function buildSectionTypes(){
|
||||
if (!window.STEREO3D) return setTimeout(buildSectionTypes, 80);
|
||||
var S = window.STEREO3D;
|
||||
|
||||
// Триангольное сечение (угловое)
|
||||
(function(){
|
||||
var sc = new S.Scene(220, 220, {view:'CABINET', scale:40});
|
||||
sc.addCube({center:[0,0,0], size:2.0, labels:false, color:'#dbeafe', opacity:0.18});
|
||||
sc.addFace([[-0.4,-1,-1],[1,0.4,-1],[1,-1,0.4]], {fill:'#fde047', opacity:0.55, stroke:'#d97706', strokeWidth:2});
|
||||
sc.addEdge([-0.4,-1,-1],[1,0.4,-1], {stroke:'#d97706', width:2.4});
|
||||
sc.addEdge([1,0.4,-1],[1,-1,0.4], {stroke:'#d97706', width:2.4});
|
||||
sc.addEdge([1,-1,0.4],[-0.4,-1,-1], {stroke:'#d97706', width:2.4});
|
||||
document.getElementById('viz3-tri').innerHTML = sc.render();
|
||||
})();
|
||||
|
||||
// Прямоугольник (плоскость пересекает 4 параллельных ребра)
|
||||
(function(){
|
||||
var sc = new S.Scene(220, 220, {view:'CABINET', scale:40});
|
||||
sc.addCube({center:[0,0,0], size:2.0, labels:false, color:'#dbeafe', opacity:0.18});
|
||||
// Плоскость y = 0 → сечение — прямоугольник (-1,0,-1),(1,0,-1),(1,0,1),(-1,0,1)
|
||||
sc.addFace([[-1,0,-1],[1,0,-1],[1,0,1],[-1,0,1]], {fill:'#86efac', opacity:0.55, stroke:'#059669', strokeWidth:2});
|
||||
sc.addEdge([-1,0,-1],[1,0,-1], {stroke:'#059669', width:2.4});
|
||||
sc.addEdge([1,0,-1],[1,0,1], {stroke:'#059669', width:2.4});
|
||||
sc.addEdge([1,0,1],[-1,0,1], {stroke:'#059669', width:2.4});
|
||||
sc.addEdge([-1,0,1],[-1,0,-1], {stroke:'#059669', width:2.4});
|
||||
document.getElementById('viz3-quad').innerHTML = sc.render();
|
||||
})();
|
||||
|
||||
// Шестиугольник (правильный, как в hero)
|
||||
(function(){
|
||||
var sc = new S.Scene(220, 220, {view:'CABINET', scale:40});
|
||||
sc.addCube({center:[0,0,0], size:2.0, labels:false, color:'#dbeafe', opacity:0.18});
|
||||
sc.addFace([[0,-1,-1],[1,0,-1],[1,1,0],[0,1,1],[-1,0,1],[-1,-1,0]],
|
||||
{fill:'#fca5a5', opacity:0.55, stroke:'#dc2626', strokeWidth:2});
|
||||
document.getElementById('viz3-hex').innerHTML = sc.render();
|
||||
})();
|
||||
}
|
||||
|
||||
function buildMethodOfTraces(){
|
||||
if (!window.STEREO3D) return setTimeout(buildMethodOfTraces, 80);
|
||||
var S = window.STEREO3D;
|
||||
var sc = new S.Scene(440, 360, {view:'CABINET', scale:60});
|
||||
sc.addCube({center:[0,0,0], size:2.0, labels:true, color:'#dbeafe', opacity:0.18});
|
||||
|
||||
// Точки M на A1B1, N на B1C1, K на CC1
|
||||
var M = [-0.3, -1, 1];
|
||||
var N = [1, 0.3, 1];
|
||||
var K = [1, 1, -0.4];
|
||||
sc.addVertex(M, 'M', {dx:-14, dy:-8, color:'#dc2626'});
|
||||
sc.addVertex(N, 'N', {dx:12, dy:-8, color:'#dc2626'});
|
||||
sc.addVertex(K, 'K', {dx:14, dy:6, color:'#dc2626'});
|
||||
|
||||
// Сечение M-N-K и достроенное
|
||||
sc.addEdge(M, N, {stroke:'#dc2626', width:2.4});
|
||||
sc.addEdge(N, K, {stroke:'#dc2626', width:2.4});
|
||||
|
||||
// След плоскости сечения на нижней грани (z=-1)
|
||||
// Прямая MN продолжена до плоскости z=-1. Параметризуем M + t(N-M):
|
||||
// M=(-0.3,-1,1), N=(1,0.3,1). z всегда 1 — параллельна нижней. Используем K и линию из K параллельную MN.
|
||||
// Возьмём точки на нижней грани: проекции M и N сдвинуты по вертикали (z=−1). Для иллюстрации проведём след — прямую на нижней грани.
|
||||
var TraceA = [-0.3, -1, -1];
|
||||
var TraceB = [1, 0.3, -1];
|
||||
sc.addEdge(TraceA, TraceB, {stroke:'#d97706', width:2.4, dash:'5 3'});
|
||||
sc.addLabel('след', [-0.5, -0.4, -1], {color:'#d97706', fontSize:13, dy:14});
|
||||
|
||||
// Соединяем K с точкой на нижней (визуальная подсказка)
|
||||
var Kproj = [1, 1, -1];
|
||||
sc.addEdge(K, Kproj, {stroke:'#94a3b8', width:1.2, dash:'4 3'});
|
||||
|
||||
document.getElementById('viz3-trace').innerHTML = sc.render();
|
||||
}
|
||||
|
||||
/* ===== Quiz items §3 ===== */
|
||||
var i3TypeItems = [
|
||||
{ q:'Плоскость пересекает три ребра, выходящие из одной вершины куба, в точках близко к вершине. Какое сечение?', opts:['Треугольник','Четырёхугольник','Шестиугольник','Невозможно'], correct:0, explain:'Угловой срез куба даёт треугольник (3 ребра — 3 точки — 3 стороны).' },
|
||||
{ q:'Плоскость параллельна одной из граней куба. Какое сечение?', opts:['Треугольник','Квадрат','Шестиугольник','Эллипс'], correct:1, explain:'Параллельное основанию сечение куба — квадрат, равный основанию.' },
|
||||
{ q:'Плоскость проходит через 6 средин рёбер, лежащих на 3 парах противоположных граней. Какое сечение?', opts:['Треугольник','Прямоугольник','Правильный шестиугольник','Окружность'], correct:2, explain:'Через 6 средин получается правильный шестиугольник — классическое сечение куба.' },
|
||||
{ q:'Может ли сечение куба быть пятиугольником?', opts:['Да','Нет','Только при наклоне'], correct:0, explain:'Да: плоскость, пересекающая 5 граней куба из 6, даёт 5-угольник.' },
|
||||
{ q:'Сечение тетраэдра — это всегда $n$-угольник, где $n \\le$ ?', opts:['3','4','5','6'], correct:1, explain:'У тетраэдра 4 грани ⇒ максимум 4-угольное сечение.' },
|
||||
{ q:'Сечение плоскостью, параллельной основанию пирамиды, подобно чему?', opts:['Боковой грани','Основанию','Высоте','Не подобно'], correct:1, explain:'Параллельное основанию сечение пирамиды подобно основанию.' }
|
||||
];
|
||||
|
||||
var i3MaxItems = [
|
||||
{ q:'Максимальное число сторон сечения куба?', answer:'6', explain:'Куб имеет 6 граней — плоскость пересечёт их максимум по 6 отрезкам.' },
|
||||
{ q:'Максимальное число сторон сечения тетраэдра?', answer:'4', explain:'У тетраэдра 4 грани ⇒ максимум 4-угольник.' },
|
||||
{ q:'Максимальное число сторон сечения шестиугольной призмы?', answer:'8', explain:'У 6-уг. призмы $6+2=8$ граней ⇒ максимум 8-угольник.' },
|
||||
{ q:'Сечение пятиугольной пирамиды — максимум $n$-угольник. Найди $n$.', answer:'5', explain:'У 5-уг. пирамиды $5+1=6$ граней, но плоскость не может пересечь основание и его параллельную плоскость — максимум 5 сторон.' },
|
||||
{ q:'У многогранника $Г = 10$ граней. Максимальное число сторон сечения?', answer:'10', explain:'Не больше числа граней — то есть 10.' }
|
||||
];
|
||||
|
||||
var i3TraceItems = [
|
||||
{ q:'Следом плоскости сечения называется линия её пересечения с одной из граней.', opts:['Верно','Неверно'], correct:0, explain:'Точно так. След — обычно с плоскостью основания.' },
|
||||
{ q:'Если плоскость параллельна основанию пирамиды, она имеет след на основании.', opts:['Верно','Неверно'], correct:1, explain:'Параллельные плоскости не пересекаются — следа на основании нет.' },
|
||||
{ q:'Зная след в основании и одну точку выше, можно построить всё сечение.', opts:['Верно','Неверно'], correct:0, explain:'Это и есть суть метода следов.' },
|
||||
{ q:'След проводится с помощью аксиомы A3 о пересечении плоскостей.', opts:['Верно','Неверно'], correct:0, explain:'Да: след — это прямая пересечения плоскости сечения с плоскостью основания.' },
|
||||
{ q:'Сечение всегда лежит в одной плоскости.', opts:['Верно','Неверно'], correct:0, explain:'По определению — плоское сечение лежит в одной (секущей) плоскости.' }
|
||||
];
|
||||
|
||||
function runQuizMC(opts){
|
||||
var state = STATE.interactives[opts.id] || { idx: 0, solved: 0 };
|
||||
var qEl = document.getElementById(opts.id + '-q');
|
||||
@@ -939,9 +1219,184 @@ var BOSS_DEFS = {
|
||||
{ q:'Сколько способов однозначно задать плоскость?', type:'mc', opts:['2','3','4','5'], correct:2, explain:'4 способа: 3 точки, прямая + точка, 2 пересек., 2 парал.' },
|
||||
{ q:'Сколько плоскостей задают 4 точки общего положения?', type:'input', a:'4', explain:'$C_4^3 = 4$ тройки точек.' }
|
||||
]
|
||||
},
|
||||
3: {
|
||||
title:'§3 — Построения сечений',
|
||||
xp:70,
|
||||
stages:[
|
||||
{ q:'Сечением куба может ли быть шестиугольник?', type:'mc', opts:['Да','Нет','Только наклонный'], correct:0, explain:'Да: классическое сечение через 6 средин рёбер.' },
|
||||
{ q:'Максимальное число сторон сечения куба?', type:'input', a:'6', explain:'6 граней ⇒ max 6 сторон.' },
|
||||
{ q:'Максимальное число сторон сечения треугольной пирамиды (тетраэдра)?', type:'input', a:'4', explain:'4 грани тетраэдра ⇒ max 4 стороны.' },
|
||||
{ q:'Плоскость, параллельная основанию пирамиды, даёт сечение, $?$ основанию.', type:'mc', opts:['Равное','Подобное','Перпендикулярное','Совпадающее'], correct:1, explain:'Подобное основанию (коэффициент подобия зависит от высоты).' },
|
||||
{ q:'Какое сечение даёт плоскость куба, проходящая через 6 средин рёбер на 3 парах противоположных граней?', type:'mc', opts:['Квадрат','Треугольник','Правильный шестиугольник','Эллипс'], correct:2, explain:'Правильный шестиугольник.' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var FINAL_BOSS_DEFS = {
|
||||
f1: {
|
||||
title:'Босс 1 · Элементы тел',
|
||||
xp:35,
|
||||
stages:[
|
||||
{ q:'Сколько рёбер у 7-угольной призмы?', type:'input', a:'21', explain:'$3n = 21$.' },
|
||||
{ q:'Сколько граней у 6-угольной пирамиды?', type:'input', a:'7', explain:'$n+1=7$.' },
|
||||
{ q:'Сколько вершин у тетраэдра?', type:'input', a:'4', explain:'4 вершины.' },
|
||||
{ q:'$В=20, Р=30$. Сколько граней по Эйлеру?', type:'input', a:'12', explain:'$20-30+Г=2 \\Rightarrow Г=12$. Это додекаэдр.' }
|
||||
]
|
||||
},
|
||||
f2: {
|
||||
title:'Босс 2 · Аксиомы',
|
||||
xp:35,
|
||||
stages:[
|
||||
{ q:'Через прямую и точку вне её проходит:', type:'mc', opts:['1 плоскость','2','Бесконечно'], correct:0, explain:'Единственная.' },
|
||||
{ q:'Две плоскости пересекаются по:', type:'mc', opts:['Точке','Прямой','Плоскости','Не пересекаются'], correct:1, explain:'По прямой (A3).' },
|
||||
{ q:'Если две точки прямой лежат в плоскости, то…', type:'mc', opts:['Прямая параллельна плоскости','Вся прямая лежит в плоскости','Прямая пересекает плоскость','Утверждение неверно'], correct:1, explain:'A2.' },
|
||||
{ q:'Скрещивающиеся прямые лежат в одной плоскости?', type:'mc', opts:['Да','Нет','Иногда'], correct:1, explain:'По определению — нет.' }
|
||||
]
|
||||
},
|
||||
f3: {
|
||||
title:'Босс 3 · Сечения',
|
||||
xp:35,
|
||||
stages:[
|
||||
{ q:'Может ли быть сечением куба пятиугольник?', type:'mc', opts:['Да','Нет','Только правильный'], correct:0, explain:'Да: плоскость пересекает 5 граней из 6.' },
|
||||
{ q:'Максимум сторон у сечения 4-угольной призмы (параллелепипеда)?', type:'input', a:'6', explain:'$n+2=4+2=6$.' },
|
||||
{ q:'Сечение параллельное основанию призмы:', type:'mc', opts:['Подобное основанию','Равное основанию','Меньше основания','Перпендикулярно основанию'], correct:1, explain:'У призмы — равное основанию.' },
|
||||
{ q:'След плоскости сечения — это:', type:'mc', opts:['Точка','Линия пересечения с гранью','Касательная','Высота сечения'], correct:1, explain:'Линия пересечения секущей плоскости с гранью (обычно — с основанием).' }
|
||||
]
|
||||
},
|
||||
f4: {
|
||||
title:'Босс 4 · Сборная',
|
||||
xp:45,
|
||||
stages:[
|
||||
{ q:'Сколько рёбер пересечёт плоскость, если сечение куба — шестиугольник?', type:'input', a:'6', explain:'Каждая сторона сечения — пересечение с одним ребром (или гранью). 6 сторон ⇒ 6 рёбер.' },
|
||||
{ q:'У многогранника $В=8, Р=12, Г=6$. Что это?', type:'mc', opts:['Тетраэдр','Куб','Октаэдр','Додекаэдр'], correct:1, explain:'Куб: $8-12+6=2$ ✓.' },
|
||||
{ q:'В пирамиде с 5-угольным основанием сечение, параллельное основанию, имеет:', type:'mc', opts:['3 стороны','4 стороны','5 сторон','6 сторон'], correct:2, explain:'Подобно основанию — тоже 5-угольник.' },
|
||||
{ q:'Сколько плоскостей задают вершины тетраэдра?', type:'input', a:'4', explain:'$C_4^3 = 4$ грани (плоскости).' },
|
||||
{ q:'У призмы $n$ боковых рёбер. У 9-угольной — сколько?', type:'input', a:'9', explain:'Ровно $n=9$.' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
function renderFinalBoss(id){
|
||||
var def = FINAL_BOSS_DEFS[id];
|
||||
if (!def) return;
|
||||
var el = document.getElementById('boss-' + id);
|
||||
if (!el) return;
|
||||
if (!STATE.bosses) STATE.bosses = {};
|
||||
var st = STATE.bosses[id] || { stage:0, defeated:false };
|
||||
STATE.bosses[id] = st;
|
||||
if (st.defeated){
|
||||
el.classList.add('victory');
|
||||
el.innerHTML = '<div class="boss-defeated">'
|
||||
+ '<div class="boss-defeated-title">' + def.title + ' побеждён!</div>'
|
||||
+ '<span class="boss-defeated-xp">+' + def.xp + ' XP</span>'
|
||||
+ '</div>';
|
||||
checkFinalComplete();
|
||||
return;
|
||||
}
|
||||
el.classList.remove('victory');
|
||||
var total = def.stages.length;
|
||||
var stage = def.stages[st.stage];
|
||||
var hp = Math.round((1 - st.stage/total) * 100);
|
||||
var optsHtml;
|
||||
if (stage.type === 'mc'){
|
||||
optsHtml = '<div class="boss-opts">';
|
||||
for (var i = 0; i < stage.opts.length; i++){
|
||||
optsHtml += '<button class="boss-opt" data-i="' + i + '">' + stage.opts[i] + '</button>';
|
||||
}
|
||||
optsHtml += '</div>';
|
||||
} else {
|
||||
optsHtml = '<div class="boss-input"><input type="text" id="boss-' + id + '-in" inputmode="text" placeholder="ответ"><button id="boss-' + id + '-go">Атака</button></div>';
|
||||
}
|
||||
el.innerHTML = '<div class="boss-h">'
|
||||
+ '<span class="boss-badge">Финал</span>'
|
||||
+ '<span class="boss-title">' + def.title + '</span>'
|
||||
+ '</div>'
|
||||
+ '<div class="boss-hp"><div class="boss-hp-label"><span>HP босса</span><span>' + hp + '%</span></div>'
|
||||
+ '<div class="boss-hp-bar"><div class="boss-hp-fill" style="width:' + hp + '%"></div></div></div>'
|
||||
+ '<div class="boss-question">'
|
||||
+ '<div class="boss-stage-label">Этап ' + (st.stage+1) + ' / ' + total + '</div>'
|
||||
+ '<div class="boss-q">' + stage.q + '</div>'
|
||||
+ optsHtml + '</div>';
|
||||
|
||||
if (stage.type === 'mc'){
|
||||
el.querySelectorAll('.boss-opt').forEach(function(btn){
|
||||
btn.addEventListener('click', function(){
|
||||
var i = parseInt(btn.getAttribute('data-i'), 10);
|
||||
var ok = (i === stage.correct);
|
||||
if (ok){
|
||||
btn.classList.add('correct');
|
||||
setTimeout(function(){ advanceFinalBoss(id); }, 600);
|
||||
} else {
|
||||
btn.classList.add('wrong');
|
||||
setTimeout(function(){ btn.classList.remove('wrong'); }, 600);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var inEl = document.getElementById('boss-' + id + '-in');
|
||||
var goEl = document.getElementById('boss-' + id + '-go');
|
||||
var box = inEl.parentNode;
|
||||
function attack(){
|
||||
var v = (inEl.value || '').trim().toLowerCase().replace(/\s+/g,'');
|
||||
var a = String(stage.a).toLowerCase().replace(/\s+/g,'');
|
||||
if (v === a){
|
||||
inEl.style.background = 'rgba(34,197,94,.25)';
|
||||
setTimeout(function(){ advanceFinalBoss(id); }, 500);
|
||||
} else {
|
||||
box.classList.add('wrong');
|
||||
inEl.style.background = 'rgba(220,38,38,.25)';
|
||||
setTimeout(function(){ box.classList.remove('wrong'); inEl.style.background=''; }, 600);
|
||||
}
|
||||
}
|
||||
goEl.addEventListener('click', attack);
|
||||
inEl.addEventListener('keydown', function(e){ if (e.key === 'Enter') attack(); });
|
||||
}
|
||||
|
||||
tryKatex(el);
|
||||
}
|
||||
|
||||
function advanceFinalBoss(id){
|
||||
var st = STATE.bosses[id];
|
||||
var def = FINAL_BOSS_DEFS[id];
|
||||
st.stage++;
|
||||
if (st.stage >= def.stages.length){
|
||||
st.defeated = true;
|
||||
saveState();
|
||||
addXp(def.xp, def.title);
|
||||
} else {
|
||||
saveState();
|
||||
}
|
||||
renderFinalBoss(id);
|
||||
}
|
||||
|
||||
function checkFinalComplete(){
|
||||
var allBeat = ['f1','f2','f3','f4'].every(function(k){
|
||||
return STATE.bosses[k] && STATE.bosses[k].defeated;
|
||||
});
|
||||
if (!allBeat) return;
|
||||
var cel = document.getElementById('celebration');
|
||||
if (!cel) return;
|
||||
if (cel.dataset.shown === '1') return;
|
||||
cel.dataset.shown = '1';
|
||||
cel.style.display = 'block';
|
||||
cel.innerHTML = '<div class="boss victory" style="text-align:center;padding:40px 24px">'
|
||||
+ '<div style="font-family:Unbounded,sans-serif;font-size:1.8rem;font-weight:900;color:#fef3c7;letter-spacing:-.01em;margin-bottom:8px">★ Раздел 1 пройден! ★</div>'
|
||||
+ '<div style="font-size:1rem;color:#dcfce7;margin-bottom:16px">Все 4 финальных босса побеждены. Введение в стереометрию — освоено.</div>'
|
||||
+ '<span class="boss-defeated-xp" style="font-size:1rem;padding:10px 22px">+ 100 XP бонус + ачивка «stereo10_r1_master»</span>'
|
||||
+ '</div>';
|
||||
// ачивка + бонус
|
||||
var achKey = 'geometry10_achievements';
|
||||
var raw = localStorage.getItem(achKey);
|
||||
var list = [];
|
||||
try { list = raw ? JSON.parse(raw) : []; } catch(e){}
|
||||
if (list.indexOf('stereo10_r1_master') < 0){
|
||||
list.push('stereo10_r1_master');
|
||||
localStorage.setItem(achKey, JSON.stringify(list));
|
||||
addXp(100, 'ачивка: Введение в стереометрию');
|
||||
}
|
||||
}
|
||||
|
||||
function renderBoss(num){
|
||||
var def = BOSS_DEFS[num];
|
||||
if (!def) return;
|
||||
@@ -1057,19 +1512,43 @@ function start(){
|
||||
buildPrismOblique();
|
||||
buildRotCube();
|
||||
buildAxiomVizes();
|
||||
buildSectionHero();
|
||||
buildSectionTypes();
|
||||
buildMethodOfTraces();
|
||||
|
||||
// Кнопка «Шаг построения»
|
||||
var stepBtn = document.getElementById('viz3-step-btn');
|
||||
if (stepBtn){
|
||||
stepBtn.addEventListener('click', function(){
|
||||
SECTION_STEP = (SECTION_STEP + 1) % 4;
|
||||
buildSectionHero();
|
||||
stepBtn.querySelector ? null : null;
|
||||
stepBtn.textContent = SECTION_STEP === 3 ? 'Сначала' : 'Шаг построения →';
|
||||
});
|
||||
}
|
||||
|
||||
runQuizMC({ id:'i1-solid', items:i1SolidItems, xpPerAll:12, title:'узнавание тел' });
|
||||
runQuizInput({ id:'i1-count', items:i1CountItems, xpPerAll:15, title:'счёт элементов' });
|
||||
runQuizMC({ id:'i2-axiom', items:i2AxiomItems, xpPerAll:12, title:'аксиомы' });
|
||||
runQuizMC({ id:'i2-plane', items:i2PlaneItems, xpPerAll:10, title:'задание плоскости' });
|
||||
runQuizMC({ id:'i2-count', items:i2CountItems, xpPerAll:10, title:'счёт плоскостей' });
|
||||
runQuizMC({ id:'i3-type', items:i3TypeItems, xpPerAll:14, title:'тип сечения' });
|
||||
runQuizInput({ id:'i3-max', items:i3MaxItems, xpPerAll:14, title:'max сторон сечения' });
|
||||
runQuizMC({ id:'i3-trace', items:i3TraceItems, xpPerAll:10, title:'метод следов' });
|
||||
|
||||
renderBoss(1);
|
||||
renderBoss(2);
|
||||
renderBoss(3);
|
||||
renderFinalBoss('f1');
|
||||
renderFinalBoss('f2');
|
||||
renderFinalBoss('f3');
|
||||
renderFinalBoss('f4');
|
||||
checkFinalComplete();
|
||||
|
||||
document.getElementById('mark-1').addEventListener('click', function(){ markRead(1); });
|
||||
document.getElementById('mark-2').addEventListener('click', function(){ markRead(2); });
|
||||
refreshMarkBtn(1); refreshMarkBtn(2);
|
||||
document.getElementById('mark-3').addEventListener('click', function(){ markRead(3); });
|
||||
refreshMarkBtn(1); refreshMarkBtn(2); refreshMarkBtn(3);
|
||||
refreshTabs();
|
||||
|
||||
var tabs = document.querySelectorAll('.sec-tab[data-tab]');
|
||||
|
||||
Reference in New Issue
Block a user