Files
Learn_System/frontend/textbooks/geometry_10_r1.html
T
Maxim Dolgolyov 0e52fedc2d 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
2026-05-29 14:54:52 +03:00

1580 lines
93 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Геометрия 10 · Раздел 1 · Введение в стереометрию</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&family=Inter:wght@400;500;600;700&family=Unbounded:wght@400;700;800;900&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<script src="/js/stereo3d.js?v=1"></script>
<style>
:root{
--bg:#f8fafc; --card:#fff; --card-2:#f1f5f9;
--text:#0b1d33; --text-2:#1e293b; --muted:#475569;
--border:#dbeafe; --border-2:#e2e8f0;
--pri:#2563eb; --pri-d:#1d4ed8; --pri-l:#93c5fd;
--pri-soft:#dbeafe; --pri-soft-2:#eff6ff;
--dark:#1e3a8a;
--accent:#dc2626;
--good:#16a34a;
--warn:#d97706;
--sh:0 4px 16px rgba(37,99,235,.08);
--sh-h:0 12px 36px rgba(37,99,235,.18);
}
html.dark{
--bg:#020617; --card:#0a1929; --card-2:#0f1f33;
--text:#dbeafe; --text-2:#bfdbfe; --muted:#94a3b8;
--border:#1e3a5f; --border-2:#1e293b;
--pri-soft:rgba(37,99,235,.18); --pri-soft-2:rgba(37,99,235,.10);
}
*{margin:0;padding:0;box-sizing:border-box}
html,body{min-height:100vh}
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.6;transition:background .25s,color .25s}
.hdr{position:relative;background:linear-gradient(110deg,var(--dark) 0%,var(--pri) 55%,var(--pri-l) 100%);color:#fff;padding:32px 24px 28px;overflow:hidden}
.hdr::before{content:'△';position:absolute;right:8px;top:-18%;font-family:'Outfit',sans-serif;font-size:clamp(8rem,22vw,18rem);font-weight:900;color:rgba(255,255,255,.10);line-height:1;pointer-events:none;user-select:none}
.hdr-inner{position:relative;z-index:1;max-width:1100px;margin:0 auto;display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.hdr-back{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;background:rgba(255,255,255,.14);border-radius:9px;color:#fff;text-decoration:none;font-size:.85rem;font-weight:600;transition:background .15s}
.hdr-back:hover{background:rgba(255,255,255,.24)}
.hdr h1{font-family:'Outfit',sans-serif;font-size:1.7rem;font-weight:900;letter-spacing:-.01em}
.hdr-sub{font-size:.92rem;opacity:.88;margin-top:4px}
.hdr-side{margin-left:auto;display:flex;gap:8px;align-items:center}
.hdr-btn{padding:8px 12px;background:rgba(255,255,255,.14);border:none;color:#fff;border-radius:9px;cursor:pointer;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s;font-family:inherit}
.hdr-btn:hover{background:rgba(255,255,255,.24)}
.hdr-xp{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:linear-gradient(135deg,#f59e0b,#dc2626);border-radius:99px;font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;letter-spacing:.04em;color:#fff;box-shadow:0 4px 12px rgba(220,38,38,.32)}
.ic{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.sec-nav{position:sticky;top:0;z-index:30;background:var(--bg);border-bottom:1px solid var(--border-2);padding:10px 16px;overflow-x:auto;white-space:nowrap;box-shadow:0 2px 8px rgba(0,0,0,.04)}
.sec-nav-inner{display:inline-flex;gap:6px;max-width:1100px;margin:0 auto}
.sec-tab{display:inline-flex;align-items:center;gap:6px;padding:7px 14px;background:var(--card-2);border:1px solid var(--border-2);border-radius:10px;font-size:.82rem;font-weight:700;color:var(--muted);cursor:pointer;transition:all .15s;text-decoration:none;font-family:inherit}
.sec-tab:hover{background:var(--pri-soft);color:var(--pri-d);border-color:var(--pri-l)}
.sec-tab.active{background:var(--pri);color:#fff;border-color:var(--pri-d)}
.sec-tab .dot{width:8px;height:8px;border-radius:50%;background:rgba(0,0,0,.15);transition:background .25s}
.sec-tab.read .dot{background:var(--good)}
.sec-tab.active .dot{background:#fff}
.sec-tab.locked{opacity:.55}
main{max-width:1100px;margin:0 auto;padding:32px 24px 80px}
.para{margin-bottom:48px;scroll-margin-top:72px}
.para-head{display:flex;align-items:flex-start;gap:16px;margin-bottom:18px}
.para-num{font-family:'Outfit',sans-serif;font-size:1.6rem;font-weight:900;color:#fff;background:linear-gradient(135deg,var(--pri),var(--pri-d));padding:10px 16px;border-radius:11px;letter-spacing:-.02em;flex-shrink:0;box-shadow:var(--sh)}
.para-h{flex:1;min-width:0}
.para-h h2{font-family:'Outfit',sans-serif;font-size:1.6rem;font-weight:800;letter-spacing:-.01em;line-height:1.2;margin-bottom:4px}
.para-h-sub{color:var(--muted);font-size:.95rem}
.theory{display:grid;grid-template-columns:1fr;gap:14px;margin-bottom:24px}
@media(min-width:760px){.theory{grid-template-columns:repeat(2,1fr)}}
.t-card{background:var(--card);border:1.5px solid var(--border-2);border-radius:13px;padding:16px 18px;box-shadow:var(--sh);transition:border-color .15s}
.t-card:hover{border-color:var(--pri-l)}
.t-tag{display:inline-block;padding:3px 9px;background:var(--pri-soft);color:var(--pri-d);border-radius:6px;font-size:.7rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;margin-bottom:8px}
.t-title{font-family:'Outfit',sans-serif;font-size:1.02rem;font-weight:800;margin-bottom:6px;color:var(--text)}
.t-body{font-size:.92rem;color:var(--text);opacity:.9;line-height:1.6}
.t-body p{margin-bottom:8px}
.t-body ul{margin:6px 0 6px 20px}
.t-body li{margin-bottom:4px}
.t-body code{background:var(--card-2);padding:2px 6px;border-radius:5px;font-family:'JetBrains Mono',monospace;font-size:.86em}
.viz{background:var(--card);border:1.5px solid var(--border-2);border-radius:14px;padding:18px;margin-bottom:18px;box-shadow:var(--sh)}
.viz-title{font-family:'Outfit',sans-serif;font-size:.94rem;font-weight:800;color:var(--text);margin-bottom:10px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.viz-title .badge{padding:2px 8px;background:var(--pri-soft);color:var(--pri-d);border-radius:5px;font-size:.7rem;letter-spacing:.04em}
.viz-cap{font-size:.84rem;color:var(--muted);margin-top:8px;line-height:1.55}
.viz-row{display:flex;gap:12px;flex-wrap:wrap;justify-content:center}
.viz-cell{flex:1 1 200px;max-width:230px;text-align:center}
.viz-cell-label{font-size:.78rem;color:var(--muted);font-weight:700;margin-top:6px}
.rot-row{display:flex;gap:14px;flex-wrap:wrap;justify-content:center;margin-top:10px;font-size:.84rem;color:var(--muted)}
.rot-row label{display:inline-flex;align-items:center;gap:6px;font-weight:600}
.inter{background:var(--card);border:1.5px solid var(--border-2);border-radius:14px;padding:18px;margin-bottom:18px;box-shadow:var(--sh)}
.inter-h{display:flex;align-items:center;gap:10px;margin-bottom:12px}
.inter-icon{width:34px;height:34px;border-radius:9px;background:var(--pri-soft);color:var(--pri-d);display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:'Outfit',sans-serif;font-weight:900;font-size:.96rem}
.inter-title{font-family:'Outfit',sans-serif;font-size:1rem;font-weight:800;flex:1}
.inter-progress{font-size:.78rem;color:var(--muted);font-weight:700;font-family:'JetBrains Mono',monospace}
.quiz{display:flex;gap:14px;align-items:center;flex-wrap:wrap}
.quiz-q{flex:1;min-width:200px;font-size:.95rem;font-weight:600;line-height:1.55}
.quiz-opts{display:flex;gap:8px;flex-wrap:wrap}
.quiz-opt{padding:8px 14px;background:var(--card-2);border:1.5px solid var(--border-2);border-radius:9px;font-weight:700;font-size:.88rem;cursor:pointer;color:var(--text);font-family:inherit;transition:all .15s}
.quiz-opt:hover{background:var(--pri-soft);border-color:var(--pri-l)}
.quiz-opt.correct{background:linear-gradient(135deg,#dcfce7,#bbf7d0);border-color:#16a34a;color:#166534;animation:pop .35s}
.quiz-opt.wrong{background:linear-gradient(135deg,#fee2e2,#fecaca);border-color:#dc2626;color:#991b1b;animation:shake .35s}
@keyframes pop{0%{transform:scale(1)}50%{transform:scale(1.07)}100%{transform:scale(1)}}
@keyframes shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-4px)}75%{transform:translateX(4px)}}
.quiz-input{display:inline-flex;align-items:center;gap:8px;background:var(--card-2);border:1.5px solid var(--border-2);border-radius:9px;padding:4px 6px;transition:border-color .15s}
.quiz-input.correct{border-color:#16a34a;background:linear-gradient(135deg,#dcfce7,#bbf7d0)}
.quiz-input.wrong{border-color:#dc2626;background:linear-gradient(135deg,#fee2e2,#fecaca)}
.quiz-input input{background:transparent;border:none;outline:none;padding:6px 8px;font-family:'JetBrains Mono',monospace;font-size:.92rem;font-weight:700;color:var(--text);width:90px}
.quiz-input button{background:var(--pri);border:none;color:#fff;padding:6px 10px;border-radius:6px;cursor:pointer;font-weight:700;font-size:.82rem;font-family:inherit}
.quiz-input button:hover{filter:brightness(1.08)}
.quiz-feedback{margin-top:10px;padding:10px 14px;background:var(--card-2);border-radius:9px;font-size:.88rem;color:var(--muted);display:none}
.quiz-feedback.show{display:block}
.quiz-feedback.good{background:linear-gradient(135deg,#dcfce7,#f0fdf4);color:#166534;border-left:3px solid #16a34a}
.quiz-feedback.bad{background:linear-gradient(135deg,#fee2e2,#fef2f2);color:#991b1b;border-left:3px solid #dc2626}
.boss{background:linear-gradient(135deg,#1e1b4b 0%,#312e81 50%,#4338ca 100%);color:#fff;border-radius:18px;padding:24px;margin-bottom:24px;position:relative;overflow:hidden;box-shadow:0 16px 48px rgba(67,56,202,.30)}
.boss::before{content:'';position:absolute;inset:0;background:radial-gradient(circle at 20% 30%,rgba(255,255,255,.10),transparent 50%);pointer-events:none}
.boss-h{display:flex;align-items:center;gap:12px;margin-bottom:14px;position:relative;z-index:1;flex-wrap:wrap}
.boss-badge{padding:5px 12px;background:rgba(245,158,11,.32);border:1px solid rgba(251,191,36,.5);border-radius:99px;font-family:'Unbounded',sans-serif;font-size:.72rem;font-weight:800;letter-spacing:.06em;color:#fde68a;text-transform:uppercase}
.boss-title{font-family:'Outfit',sans-serif;font-size:1.2rem;font-weight:800;flex:1}
.boss-hp{margin-bottom:14px;position:relative;z-index:1}
.boss-hp-label{display:flex;justify-content:space-between;font-size:.78rem;font-weight:700;margin-bottom:5px;color:#e0e7ff}
.boss-hp-bar{height:12px;background:rgba(255,255,255,.10);border-radius:7px;overflow:hidden;border:1px solid rgba(255,255,255,.18)}
.boss-hp-fill{height:100%;background:linear-gradient(90deg,#f59e0b,#ef4444);transition:width .5s ease-out}
.boss-question{background:rgba(0,0,0,.32);border-radius:12px;padding:16px;margin-bottom:14px;position:relative;z-index:1;border:1px solid rgba(255,255,255,.10)}
.boss-stage-label{font-size:.74rem;color:#a5b4fc;font-weight:700;letter-spacing:.08em;text-transform:uppercase;margin-bottom:6px}
.boss-q{font-size:1.02rem;font-weight:700;margin-bottom:14px;line-height:1.55}
.boss-opts{display:flex;gap:8px;flex-wrap:wrap}
.boss-opt{padding:10px 16px;background:rgba(255,255,255,.10);border:1.5px solid rgba(255,255,255,.20);border-radius:9px;color:#fff;font-weight:700;font-size:.92rem;cursor:pointer;font-family:inherit;transition:all .15s}
.boss-opt:hover{background:rgba(255,255,255,.18);border-color:rgba(255,255,255,.4)}
.boss-opt.correct{background:linear-gradient(135deg,#16a34a,#22c55e);border-color:#86efac;animation:pop .35s}
.boss-opt.wrong{background:linear-gradient(135deg,#dc2626,#ef4444);border-color:#fca5a5;animation:shake .35s}
.boss-input{display:inline-flex;align-items:center;gap:6px;background:rgba(0,0,0,.36);border:1.5px solid rgba(255,255,255,.20);border-radius:9px;padding:4px 6px}
.boss-input.wrong{border-color:#fca5a5;animation:shake .35s}
.boss-input input{background:transparent;border:none;outline:none;color:#fff;font-family:'JetBrains Mono',monospace;font-size:.95rem;font-weight:700;padding:6px 8px;width:100px}
.boss-input button{background:#f59e0b;border:none;color:#fff;padding:6px 12px;border-radius:6px;cursor:pointer;font-weight:800;font-size:.82rem;font-family:inherit}
.boss-defeated{text-align:center;padding:16px;position:relative;z-index:1}
.boss-defeated-title{font-family:'Outfit',sans-serif;font-size:1.3rem;font-weight:900;color:#fde68a;margin-bottom:8px;letter-spacing:-.01em}
.boss-defeated-sub{font-size:.92rem;color:#c7d2fe;margin-bottom:14px}
.boss-defeated-xp{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;background:linear-gradient(135deg,#f59e0b,#dc2626);border-radius:99px;font-family:'Unbounded',sans-serif;font-weight:800;font-size:.86rem;letter-spacing:.06em;box-shadow:0 6px 20px rgba(245,158,11,.4)}
.boss.victory{background:linear-gradient(135deg,#15803d,#22c55e 50%,#86efac)}
.boss.victory::before{background:radial-gradient(circle at 50% 50%,rgba(254,240,138,.20),transparent 70%)}
.para-actions{display:flex;justify-content:center;margin-top:18px;gap:10px}
.mark-btn{padding:11px 22px;background:var(--pri);color:#fff;border:none;border-radius:11px;font-weight:800;font-size:.92rem;cursor:pointer;font-family:'Inter',sans-serif;display:inline-flex;align-items:center;gap:8px;transition:filter .15s,transform .15s;box-shadow:var(--sh)}
.mark-btn:hover{filter:brightness(1.10);transform:translateY(-1px)}
.mark-btn.done{background:linear-gradient(135deg,var(--good),#22c55e)}
.stub-card{background:linear-gradient(135deg,var(--pri-soft),transparent);border:1px dashed var(--pri-l);border-radius:14px;padding:24px;text-align:center;color:var(--pri-d);margin-bottom:24px}
.stub-card b{font-family:'Outfit',sans-serif;font-size:1.1rem;display:block;margin-bottom:4px}
.stub-card small{display:block;margin-top:6px;color:var(--muted);font-size:.84rem}
.foot{text-align:center;padding:24px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border-2);margin-top:30px}
@media (max-width:560px){
.hdr h1{font-size:1.35rem}
.hdr-sub{font-size:.85rem}
.para-num{font-size:1.3rem;padding:8px 12px}
.para-h h2{font-size:1.3rem}
.quiz{flex-direction:column;align-items:flex-start}
}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-inner">
<div>
<a href="/textbook/geometry-10" class="hdr-back">
<svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
К курсу
</a>
</div>
<div>
<h1>Раздел 1. Введение в стереометрию</h1>
<div class="hdr-sub">Пространственные фигуры · Аксиомы · Сечения</div>
</div>
<div class="hdr-side">
<span class="hdr-xp" id="hdr-xp">0 XP</span>
<button id="theme-btn" class="hdr-btn" title="Сменить тему">
<svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
<span id="theme-lab">Тёмная</span>
</button>
</div>
</div>
</header>
<nav class="sec-nav">
<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" 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>
<main>
<section id="para-1" class="para">
<div class="para-head">
<div class="para-num">§ 1</div>
<div class="para-h">
<h2>Пространственные фигуры</h2>
<div class="para-h-sub">Многогранники и тела вращения · грани, рёбра, вершины · формула Эйлера</div>
</div>
</div>
<div class="viz">
<div class="viz-title"><span class="badge">5 ОСНОВНЫХ ТЕЛ</span> Знакомство со стереометрией</div>
<div class="viz-row" id="viz1-solids"></div>
<div class="viz-cap">Стереометрия изучает фигуры в трёхмерном пространстве. Слева направо: <b>призма</b>, <b>пирамида</b>, <b>цилиндр</b>, <b>конус</b>, <b>шар</b>. Первые две — <b>многогранники</b> (все грани плоские), три остальные — <b>тела вращения</b>.</div>
</div>
<div class="theory">
<div class="t-card">
<span class="t-tag">1.1</span>
<div class="t-title">Многогранник: грани, рёбра, вершины</div>
<div class="t-body">
<p><b>Многогранник</b> — тело, ограниченное конечным числом плоских многоугольников.</p>
<ul>
<li><b>Грань</b> — каждый из этих многоугольников.</li>
<li><b>Ребро</b> — общая сторона двух соседних граней.</li>
<li><b>Вершина</b> — общая точка трёх или более рёбер.</li>
<li><b>Диагональ</b> — отрезок, соединяющий две вершины, не лежащие на одной грани.</li>
</ul>
</div>
</div>
<div class="t-card">
<span class="t-tag">1.2</span>
<div class="t-title">Призма</div>
<div class="t-body">
<p><b>Призма</b> — многогранник, у которого две грани (<b>основания</b>) — равные многоугольники в параллельных плоскостях, а остальные грани (<b>боковые</b>) — параллелограммы.</p>
<p><b>Виды:</b> прямая (боковые рёбра ⊥ основанию) или наклонная; правильная — прямая с основанием в виде правильного $n$-угольника.</p>
<p>Параллелепипед — частный случай призмы (основание — параллелограмм).</p>
</div>
</div>
<div class="t-card">
<span class="t-tag">1.3</span>
<div class="t-title">Пирамида</div>
<div class="t-body">
<p><b>Пирамида</b> — многогранник с одним основанием-многоугольником и боковыми гранями-треугольниками с общей <b>вершиной пирамиды</b>.</p>
<p>Правильная пирамида: основание — правильный $n$-угольник, вершина проектируется в его центр.</p>
</div>
</div>
<div class="t-card">
<span class="t-tag">1.4</span>
<div class="t-title">Тела вращения</div>
<div class="t-body">
<p><b>Цилиндр</b> — вращение прямоугольника вокруг одной из его сторон.</p>
<p><b>Конус</b> — вращение прямоугольного треугольника вокруг катета.</p>
<p><b>Шар</b> — вращение полукруга вокруг диаметра.</p>
</div>
</div>
<div class="t-card">
<span class="t-tag">1.5</span>
<div class="t-title">Формула Эйлера</div>
<div class="t-body">
<p>Для любого <b>выпуклого многогранника</b>:</p>
<p style="text-align:center;font-size:1.05rem;margin:8px 0">$$ В - Р + Г = 2 $$</p>
<p>где В — число вершин, Р — рёбер, Г — граней. Куб: $В=8, Р=12, Г=6 \Rightarrow 8-12+6=2$. ✓</p>
</div>
</div>
<div class="t-card">
<span class="t-tag">1.6</span>
<div class="t-title">Изображение на плоскости</div>
<div class="t-body">
<p>Пространственные фигуры изображают на плоскости в параллельной проекции (чаще — <b>кабинетной</b>).</p>
<p>Видимые рёбра — <b>сплошной</b> линией, невидимые — <b>штриховой</b>.</p>
<p>Параллельные отрезки остаются параллельными, но длины и углы искажаются.</p>
</div>
</div>
</div>
<div class="viz">
<div class="viz-title"><span class="badge">КУБ</span> Грани · рёбра · вершины · диагональ</div>
<div id="viz1-cube"></div>
<div class="viz-cap">Куб $ABCDA_1B_1C_1D_1$. Видимые рёбра — сплошные, невидимые — штриховые. Подсвечена <span style="color:#dc2626;font-weight:800">пространственная диагональ $AC_1$</span> и одна <span style="color:#7c3aed;font-weight:800">боковая грань $ABB_1A_1$</span>.</div>
</div>
<div class="viz">
<div class="viz-title"><span class="badge">ПРИЗМЫ</span> Прямая и наклонная</div>
<div class="viz-row">
<div class="viz-cell"><div id="viz1-prism-direct"></div><div class="viz-cell-label">Прямая (правильная)</div></div>
<div class="viz-cell"><div id="viz1-prism-oblique"></div><div class="viz-cell-label">Наклонная</div></div>
</div>
<div class="viz-cap">У прямой призмы боковые рёбра перпендикулярны основанию; у наклонной — нет. Правильная — прямая с правильным $n$-угольником в основании.</div>
</div>
<div class="inter" data-inter="i1-solid">
<div class="inter-h">
<div class="inter-icon">1</div>
<div class="inter-title">Узнай тело по описанию</div>
<div class="inter-progress" id="i1-solid-prog">0 / 6</div>
</div>
<div class="quiz">
<div class="quiz-q" id="i1-solid-q"></div>
<div class="quiz-opts" id="i1-solid-opts"></div>
</div>
<div class="quiz-feedback" id="i1-solid-fb"></div>
</div>
<div class="inter" data-inter="i1-count">
<div class="inter-h">
<div class="inter-icon">2</div>
<div class="inter-title">Сосчитай элементы</div>
<div class="inter-progress" id="i1-count-prog">0 / 6</div>
</div>
<div class="quiz">
<div class="quiz-q" id="i1-count-q"></div>
<div class="quiz-input">
<input id="i1-count-in" type="text" inputmode="numeric" placeholder="?">
<button id="i1-count-go">OK</button>
</div>
</div>
<div class="quiz-feedback" id="i1-count-fb"></div>
</div>
<div class="inter" data-inter="i1-rot">
<div class="inter-h">
<div class="inter-icon">3</div>
<div class="inter-title">Куб с разных сторон</div>
<div class="inter-progress" id="i1-rot-prog">смотри 3D</div>
</div>
<div id="i1-rot-viz" style="text-align:center"></div>
<div class="rot-row">
<label>Поворот X <input id="i1-rot-x" type="range" min="-60" max="60" value="0" step="1"> <span id="i1-rot-xv" style="font-family:JetBrains Mono,monospace;color:var(--pri-d)"></span></label>
<label>Поворот Y <input id="i1-rot-y" type="range" min="-60" max="60" value="0" step="1"> <span id="i1-rot-yv" style="font-family:JetBrains Mono,monospace;color:var(--pri-d)"></span></label>
</div>
<div class="viz-cap">Покрути куб — видимые рёбра останутся сплошными, невидимые — пунктирными. Это «кабинетная» проекция: $y$-ось уходит вглубь под $30°$ со сжатием $\tfrac{1}{2}$.</div>
</div>
<div class="boss" id="boss-1"></div>
<div class="para-actions">
<button class="mark-btn" id="mark-1">
<svg class="ic" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>
<span>Отметить §1 как изученный</span>
</button>
</div>
</section>
<section id="para-2" class="para">
<div class="para-head">
<div class="para-num">§ 2</div>
<div class="para-h">
<h2>Прямые и плоскости в пространстве</h2>
<div class="para-h-sub">Три аксиомы стереометрии и их следствия · 4 способа задания плоскости</div>
</div>
</div>
<div class="viz">
<div class="viz-title"><span class="badge">АКСИОМЫ A1A3</span> Основа стереометрии</div>
<div class="viz-row">
<div class="viz-cell"><div id="viz2-a1"></div><div class="viz-cell-label">A1: три точки</div></div>
<div class="viz-cell"><div id="viz2-a2"></div><div class="viz-cell-label">A2: прямая в плоскости</div></div>
<div class="viz-cell"><div id="viz2-a3"></div><div class="viz-cell-label">A3: пересечение</div></div>
</div>
<div class="viz-cap">Три аксиомы стереометрии: через 3 точки — единственная плоскость; прямая лежит в плоскости, если 2 её точки в ней; две плоскости пересекаются по прямой.</div>
</div>
<div class="theory">
<div class="t-card">
<span class="t-tag">A1</span>
<div class="t-title">Через три точки</div>
<div class="t-body">
<p>Через любые три точки, <b>не лежащие на одной прямой</b>, проходит <b>единственная</b> плоскость.</p>
<p>Если же точки коллинеарны — плоскостей бесконечно много (можно «вращать» вокруг прямой).</p>
</div>
</div>
<div class="t-card">
<span class="t-tag">A2</span>
<div class="t-title">Прямая в плоскости</div>
<div class="t-body">
<p>Если две точки прямой лежат в плоскости, то и <b>вся прямая</b> лежит в этой плоскости.</p>
<p>Обозначение: $a \subset \alpha$ — «прямая $a$ лежит в плоскости $\alpha$».</p>
</div>
</div>
<div class="t-card">
<span class="t-tag">A3</span>
<div class="t-title">Пересечение плоскостей</div>
<div class="t-body">
<p>Если две плоскости имеют общую точку, то они имеют <b>общую прямую</b>, на которой лежат все их общие точки.</p>
<p>Эту прямую называют <b>линией пересечения</b> плоскостей: $\alpha \cap \beta = l$.</p>
</div>
</div>
<div class="t-card">
<span class="t-tag">2.4</span>
<div class="t-title">Следствия из аксиом</div>
<div class="t-body">
<ul>
<li>Через <b>прямую и точку</b> вне её — единственная плоскость.</li>
<li>Через <b>две пересекающиеся</b> прямые — единственная плоскость.</li>
<li>Через <b>две параллельные</b> прямые — единственная плоскость.</li>
</ul>
</div>
</div>
<div class="t-card">
<span class="t-tag">2.5</span>
<div class="t-title">4 способа задать плоскость</div>
<div class="t-body">
<ul>
<li>3 точки, не лежащие на одной прямой;</li>
<li>прямая и точка вне её;</li>
<li>две пересекающиеся прямые;</li>
<li>две параллельные прямые.</li>
</ul>
<p>Все эти способы — переформулировки аксиомы A1.</p>
</div>
</div>
<div class="t-card">
<span class="t-tag">2.6</span>
<div class="t-title">Обозначения</div>
<div class="t-body">
<ul>
<li>$A \in \alpha$ — точка $A$ принадлежит плоскости $\alpha$;</li>
<li>$a \subset \alpha$ — прямая $a$ лежит в $\alpha$;</li>
<li>$\alpha \cap \beta = c$ — плоскости пересекаются по прямой $c$;</li>
<li>$a \parallel b$ — прямые параллельны.</li>
</ul>
</div>
</div>
</div>
<div class="viz">
<div class="viz-title"><span class="badge">СЛЕДСТВИЯ</span> 3 способа однозначно задать плоскость</div>
<div class="viz-row">
<div class="viz-cell"><div id="viz2-c1"></div><div class="viz-cell-label">Прямая + точка</div></div>
<div class="viz-cell"><div id="viz2-c2"></div><div class="viz-cell-label">Пересекающиеся прямые</div></div>
<div class="viz-cell"><div id="viz2-c3"></div><div class="viz-cell-label">Параллельные прямые</div></div>
</div>
</div>
<div class="inter" data-inter="i2-axiom">
<div class="inter-h">
<div class="inter-icon">1</div>
<div class="inter-title">Какая аксиома или следствие?</div>
<div class="inter-progress" id="i2-axiom-prog">0 / 6</div>
</div>
<div class="quiz">
<div class="quiz-q" id="i2-axiom-q"></div>
<div class="quiz-opts" id="i2-axiom-opts"></div>
</div>
<div class="quiz-feedback" id="i2-axiom-fb"></div>
</div>
<div class="inter" data-inter="i2-plane">
<div class="inter-h">
<div class="inter-icon">2</div>
<div class="inter-title">Можно ли задать плоскость?</div>
<div class="inter-progress" id="i2-plane-prog">0 / 5</div>
</div>
<div class="quiz">
<div class="quiz-q" id="i2-plane-q"></div>
<div class="quiz-opts" id="i2-plane-opts"></div>
</div>
<div class="quiz-feedback" id="i2-plane-fb"></div>
</div>
<div class="inter" data-inter="i2-count">
<div class="inter-h">
<div class="inter-icon">3</div>
<div class="inter-title">Сколько плоскостей?</div>
<div class="inter-progress" id="i2-count-prog">0 / 5</div>
</div>
<div class="quiz">
<div class="quiz-q" id="i2-count-q"></div>
<div class="quiz-opts" id="i2-count-opts"></div>
</div>
<div class="quiz-feedback" id="i2-count-fb"></div>
</div>
<div class="boss" id="boss-2"></div>
<div class="para-actions">
<button class="mark-btn" id="mark-2">
<svg class="ic" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>
<span>Отметить §2 как изученный</span>
</button>
</div>
</section>
<section id="para-3" class="para">
<div class="para-head">
<div class="para-num">§ 3</div>
<div class="para-h">
<h2>Построения сечений</h2>
<div class="para-h-sub">Метод следов · сечения куба, призмы, пирамиды</div>
</div>
</div>
<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>
<section id="para-final" class="para">
<div class="para-head">
<div class="para-num"></div>
<div class="para-h">
<h2>Финал раздела 1</h2>
<div class="para-h-sub">4 интегральных босса · ачивка «Введение в стереометрию пройдено»</div>
</div>
</div>
<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>
<footer class="foot">
Геометрия — 10 класс · Раздел 1 · LearnSpace
</footer>
<script>
'use strict';
(function(){
var saved = localStorage.getItem('geometry10_theme') || localStorage.getItem('theme') || 'light';
if (saved === 'dark') document.documentElement.classList.add('dark');
var lab = document.getElementById('theme-lab');
if (lab) lab.textContent = saved === 'dark' ? 'Светлая' : 'Тёмная';
document.getElementById('theme-btn').addEventListener('click', function(){
document.documentElement.classList.toggle('dark');
var dark = document.documentElement.classList.contains('dark');
localStorage.setItem('geometry10_theme', dark ? 'dark' : 'light');
localStorage.setItem('theme', dark ? 'dark' : 'light');
if (lab) lab.textContent = dark ? 'Светлая' : 'Тёмная';
});
})();
var STATE_KEY = 'geometry10_r1_state';
var STATE = (function(){
try { var raw = localStorage.getItem(STATE_KEY); if (raw) return JSON.parse(raw); } catch(e){}
return { read:[], bosses:{}, interactives:{} };
})();
function saveState(){ try { localStorage.setItem(STATE_KEY, JSON.stringify(STATE)); } catch(e){} }
function addXp(amount, reason){
var cur = parseInt(localStorage.getItem('geometry10_xp') || '0', 10) || 0;
cur += amount;
localStorage.setItem('geometry10_xp', String(cur));
updateXpBadge();
if (window.LS && window.LS.xp && typeof window.LS.xp.toast === 'function'){
window.LS.xp.toast('+' + amount + ' XP — ' + (reason || ''));
}
}
function updateXpBadge(){
var el = document.getElementById('hdr-xp');
if (el) el.textContent = (parseInt(localStorage.getItem('geometry10_xp')||'0',10)||0) + ' XP';
}
updateXpBadge();
var syncTimer = null;
function syncProgress(){
if (syncTimer) clearTimeout(syncTimer);
syncTimer = setTimeout(function(){
if (!window.LS || typeof window.LS.api !== 'function') return;
window.LS.api('/api/textbooks/geometry-10-r1/progress', {
method:'POST',
body: JSON.stringify({ read: STATE.read })
}).catch(function(){});
}, 600);
}
function markRead(paraNum){
if (STATE.read.indexOf(paraNum) < 0){
STATE.read.push(paraNum);
saveState();
syncProgress();
}
refreshTabs();
refreshMarkBtn(paraNum);
}
function refreshMarkBtn(n){
var btn = document.getElementById('mark-' + n);
if (!btn) return;
if (STATE.read.indexOf(n) >= 0){
btn.classList.add('done');
btn.querySelector('span').textContent = '§' + n + ' изучен';
}
}
function refreshTabs(){
document.querySelectorAll('.sec-tab').forEach(function(t){
var n = t.getAttribute('data-tab');
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');
}
});
}
function buildHeroSolids(){
if (!window.STEREO3D) return setTimeout(buildHeroSolids, 80);
var S = window.STEREO3D;
var defs = [
{ label:'Призма (6-уг)', fn:function(sc){sc.addPrism({n:6,baseRadius:1.1,height:1.8,color:'#dbeafe'});} },
{ label:'Пирамида (4-уг)', fn:function(sc){sc.addPyramid({n:4,baseRadius:1.2,height:1.9,color:'#fee2e2'});} },
{ label:'Цилиндр', fn:function(sc){sc.addCylinder({segments:28,baseRadius:1.0,height:1.9,color:'#d1fae5'});} },
{ label:'Конус', fn:function(sc){sc.addCone({segments:28,baseRadius:1.1,height:2.0,color:'#fef3c7'});} },
{ label:'Шар', fn:function(sc){sc.addSphere({center:[0,0,0],radius:1.2,color:'#ede9fe'});} }
];
var row = document.getElementById('viz1-solids');
if (!row) return;
row.innerHTML = '';
for (var i = 0; i < defs.length; i++){
var sc = new S.Scene(180, 170, {view:'CABINET', scale:34, bg:'transparent', border:'none'});
defs[i].fn(sc);
var cell = document.createElement('div');
cell.className = 'viz-cell';
cell.innerHTML = sc.render() + '<div class="viz-cell-label">' + defs[i].label + '</div>';
row.appendChild(cell);
}
}
function buildAnnotatedCube(){
if (!window.STEREO3D) return setTimeout(buildAnnotatedCube, 80);
var S = window.STEREO3D;
var sc = new S.Scene(420, 320, {view:'CABINET', scale:55});
sc.addCube({center:[0,0,0], size:2.0, labels:true});
sc.addEdge([-1,-1,-1],[+1,+1,+1], {stroke:'#dc2626', width:3, dash:'5 3'});
sc.addFace([[-1,-1,-1],[1,-1,-1],[1,-1,1],[-1,-1,1]], {fill:'#a78bfa', opacity:0.30, stroke:'none'});
document.getElementById('viz1-cube').innerHTML = sc.render();
}
function buildPrismDirect(){
if (!window.STEREO3D) return setTimeout(buildPrismDirect, 80);
var S = window.STEREO3D;
var sc = new S.Scene(220, 220, {view:'CABINET', scale:36});
sc.addPrism({n:6, baseRadius:1.2, height:2.0, color:'#dbeafe'});
document.getElementById('viz1-prism-direct').innerHTML = sc.render();
}
function buildPrismOblique(){
if (!window.STEREO3D) return setTimeout(buildPrismOblique, 80);
var S = window.STEREO3D;
var sc = new S.Scene(220, 220, {view:'CABINET', scale:36});
var n = 6, r = 1.2, h = 2.0, shift = 0.7;
var bottom = [], top = [];
for (var i = 0; i < n; i++){
var a = 2 * Math.PI * i / n - Math.PI/2;
bottom.push([r*Math.cos(a), r*Math.sin(a), -h/2]);
top.push([r*Math.cos(a) + shift, r*Math.sin(a), +h/2]);
}
var verts = bottom.concat(top);
var faces = [];
var bi = []; for (var k = 0; k < n; k++) bi.push(k);
var ti = []; for (var k2 = 0; k2 < n; k2++) ti.push(k2 + n);
faces.push(bi); faces.push(ti.slice().reverse());
for (var j = 0; j < n; j++){
var jj = (j+1) % n;
faces.push([j, jj, jj+n, j+n]);
}
var edges = [];
for (var e = 0; e < n; e++){
var ee = (e+1) % n;
edges.push([e, ee]);
edges.push([e+n, ee+n]);
edges.push([e, e+n]);
}
sc._addPolyhedron(verts, faces, edges, [], {color:'#fef3c7', opacity:0.4});
document.getElementById('viz1-prism-oblique').innerHTML = sc.render();
}
function buildRotCube(){
if (!window.STEREO3D) return setTimeout(buildRotCube, 80);
var S = window.STEREO3D;
var holder = document.getElementById('i1-rot-viz');
var rx = document.getElementById('i1-rot-x');
var ry = document.getElementById('i1-rot-y');
var rxv = document.getElementById('i1-rot-xv');
var ryv = document.getElementById('i1-rot-yv');
function draw(){
var ax = parseFloat(rx.value) * Math.PI / 180;
var ay = parseFloat(ry.value) * Math.PI / 180;
rxv.textContent = rx.value + '°';
ryv.textContent = ry.value + '°';
var sc = new S.Scene(360, 280, {view:'CABINET', scale:56, rotX:ax, rotY:ay});
sc.addCube({center:[0,0,0], size:2.0, labels:true, color:'#dbeafe'});
holder.innerHTML = sc.render();
}
rx.addEventListener('input', draw);
ry.addEventListener('input', draw);
draw();
}
function buildAxiomVizes(){
if (!window.STEREO3D) return setTimeout(buildAxiomVizes, 80);
var S = window.STEREO3D;
(function(){
var sc = new S.Scene(220, 200, {view:'CABINET', scale:42});
sc.addPlane([0,0,0], [0,0,1], {size:2.0, label:'α'});
sc.addVertex([-1.0, -0.6, 0], 'A', {dx:-14, dy:-8, color:'#dc2626'});
sc.addVertex([+1.0, -0.4, 0], 'B', {dx:8, dy:-8, color:'#dc2626'});
sc.addVertex([+0.2, +1.0, 0], 'C', {dx:8, dy:-8, color:'#dc2626'});
document.getElementById('viz2-a1').innerHTML = sc.render();
})();
(function(){
var sc = new S.Scene(220, 200, {view:'CABINET', scale:42});
sc.addPlane([0,0,0], [0,0,1], {size:2.0, label:'α'});
sc.addEdge([-1.5, -0.8, 0], [+1.5, +0.8, 0], {stroke:'#dc2626', width:2.6});
sc.addLabel('a', [+1.5, +0.8, 0], {dx:14, dy:-4, color:'#dc2626', fontSize:15, anchor:'start'});
sc.addVertex([-1.0, -0.5, 0], 'M', {dx:-16, dy:-8, color:'#1e3a8a'});
sc.addVertex([+1.0, +0.5, 0], 'N', {dx:8, dy:-8, color:'#1e3a8a'});
document.getElementById('viz2-a2').innerHTML = sc.render();
})();
(function(){
var sc = new S.Scene(220, 200, {view:'CABINET', scale:42});
sc.addPlane([0,0,0], [0,0,1], {size:1.6, label:'α', opacity:0.20});
sc.addPlane([0,0,0], [Math.cos(Math.PI/4),0,Math.sin(Math.PI/4)], {size:1.6, label:'β', opacity:0.22, fill:'#fde047'});
sc.addEdge([-1.4, 0, 0], [+1.4, 0, 0], {stroke:'#dc2626', width:2.8});
sc.addLabel('l', [+1.4, 0, 0], {dx:14, dy:-4, color:'#dc2626', fontSize:15, anchor:'start'});
document.getElementById('viz2-a3').innerHTML = sc.render();
})();
(function(){
var sc = new S.Scene(220, 200, {view:'CABINET', scale:42});
sc.addPlane([0,0,0], [0,0,1], {size:2.0, label:'α'});
sc.addEdge([-1.5, -0.4, 0], [+1.5, +0.4, 0], {stroke:'#1e3a8a', width:2.4});
sc.addLabel('a', [+1.5, +0.4, 0], {dx:14, dy:-4, color:'#1e3a8a', fontSize:14, anchor:'start'});
sc.addVertex([+0.4, -0.9, 0], 'M', {dx:8, dy:-8, color:'#dc2626'});
document.getElementById('viz2-c1').innerHTML = sc.render();
})();
(function(){
var sc = new S.Scene(220, 200, {view:'CABINET', scale:42});
sc.addPlane([0,0,0], [0,0,1], {size:2.0, label:'α'});
sc.addEdge([-1.5, -0.8, 0], [+1.5, +0.8, 0], {stroke:'#1e3a8a', width:2.4});
sc.addEdge([-1.0, +0.9, 0], [+1.1, -1.0, 0], {stroke:'#dc2626', width:2.4});
sc.addVertex([+0.05, 0.04, 0], 'O', {dx:8, dy:-10, color:'#0b1d33'});
sc.addLabel('a', [+1.5, +0.8, 0], {dx:12, dy:-4, color:'#1e3a8a', fontSize:14, anchor:'start'});
sc.addLabel('b', [+1.1, -1.0, 0], {dx:12, dy:-4, color:'#dc2626', fontSize:14, anchor:'start'});
document.getElementById('viz2-c2').innerHTML = sc.render();
})();
(function(){
var sc = new S.Scene(220, 200, {view:'CABINET', scale:42});
sc.addPlane([0,0,0], [0,0,1], {size:2.0, label:'α'});
sc.addEdge([-1.5, -0.6, 0], [+1.5, -0.6, 0], {stroke:'#1e3a8a', width:2.4});
sc.addEdge([-1.5, +0.6, 0], [+1.5, +0.6, 0], {stroke:'#dc2626', width:2.4});
sc.addLabel('a', [+1.5, -0.6, 0], {dx:12, dy:-4, color:'#1e3a8a', fontSize:14, anchor:'start'});
sc.addLabel('b', [+1.5, +0.6, 0], {dx:12, dy:-4, color:'#dc2626', fontSize:14, anchor:'start'});
document.getElementById('viz2-c3').innerHTML = sc.render();
})();
}
/* ===== §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');
var optsEl = document.getElementById(opts.id + '-opts');
var fbEl = document.getElementById(opts.id + '-fb');
var progEl = document.getElementById(opts.id + '-prog');
function render(){
if (state.solved >= opts.items.length){
qEl.innerHTML = '<b style="color:var(--good)">Все задания решены!</b> +' + (opts.xpPerAll || 10) + ' XP.';
optsEl.innerHTML = '';
fbEl.classList.remove('show');
progEl.textContent = opts.items.length + ' / ' + opts.items.length;
if (!state.awarded){
state.awarded = true;
STATE.interactives[opts.id] = state;
saveState();
addXp(opts.xpPerAll || 10, opts.title || 'тренажёр');
}
return;
}
var item = opts.items[state.idx % opts.items.length];
qEl.innerHTML = item.q;
optsEl.innerHTML = '';
fbEl.classList.remove('show');
for (var i = 0; i < item.opts.length; i++){
(function(i){
var btn = document.createElement('button');
btn.className = 'quiz-opt';
btn.innerHTML = item.opts[i];
btn.addEventListener('click', function(){
var correct = (i === item.correct);
if (correct){
btn.classList.add('correct');
state.solved++;
state.idx++;
STATE.interactives[opts.id] = state;
saveState();
progEl.textContent = state.solved + ' / ' + opts.items.length;
fbEl.className = 'quiz-feedback show good';
fbEl.innerHTML = '<b>Верно.</b> ' + (item.explain || '');
tryKatex(fbEl);
setTimeout(render, 900);
} else {
btn.classList.add('wrong');
fbEl.className = 'quiz-feedback show bad';
fbEl.innerHTML = '<b>Не так.</b> ' + (item.explain || '');
tryKatex(fbEl);
}
});
optsEl.appendChild(btn);
})(i);
}
progEl.textContent = state.solved + ' / ' + opts.items.length;
tryKatex(qEl);
}
render();
}
function runQuizInput(opts){
var state = STATE.interactives[opts.id] || { idx:0, solved:0 };
var qEl = document.getElementById(opts.id + '-q');
var inEl = document.getElementById(opts.id + '-in');
var goEl = document.getElementById(opts.id + '-go');
var fbEl = document.getElementById(opts.id + '-fb');
var progEl = document.getElementById(opts.id + '-prog');
var box = inEl.parentNode;
function render(){
if (state.solved >= opts.items.length){
qEl.innerHTML = '<b style="color:var(--good)">Все задания решены!</b> +' + (opts.xpPerAll || 10) + ' XP.';
inEl.value = ''; inEl.disabled = true; goEl.disabled = true;
progEl.textContent = opts.items.length + ' / ' + opts.items.length;
box.className = 'quiz-input correct';
if (!state.awarded){
state.awarded = true;
STATE.interactives[opts.id] = state;
saveState();
addXp(opts.xpPerAll || 10, opts.title || 'тренажёр');
}
return;
}
var item = opts.items[state.idx % opts.items.length];
qEl.innerHTML = item.q;
inEl.value = ''; inEl.disabled = false; goEl.disabled = false;
fbEl.classList.remove('show');
box.className = 'quiz-input';
progEl.textContent = state.solved + ' / ' + opts.items.length;
tryKatex(qEl);
}
function check(){
var item = opts.items[state.idx % opts.items.length];
var v = (inEl.value || '').trim().toLowerCase().replace(/\s+/g,'');
var ans = String(item.answer).toLowerCase().replace(/\s+/g,'');
if (v === ans){
box.className = 'quiz-input correct';
state.solved++;
state.idx++;
STATE.interactives[opts.id] = state;
saveState();
fbEl.className = 'quiz-feedback show good';
fbEl.innerHTML = '<b>Верно.</b> ' + (item.explain || '');
tryKatex(fbEl);
setTimeout(render, 900);
} else {
box.className = 'quiz-input wrong';
fbEl.className = 'quiz-feedback show bad';
fbEl.innerHTML = '<b>Не так.</b> ' + (item.explain || '');
tryKatex(fbEl);
}
}
goEl.addEventListener('click', check);
inEl.addEventListener('keydown', function(e){ if (e.key === 'Enter') check(); });
render();
}
var i1SolidItems = [
{ q:'Тело с двумя равными $n$-угольными гранями в параллельных плоскостях и $n$ параллелограммами по бокам.', opts:['Призма','Пирамида','Цилиндр','Конус'], correct:0, explain:'Это определение призмы.' },
{ q:'Тело с одной многоугольной гранью и треугольными боковыми гранями с общей вершиной.', opts:['Призма','Пирамида','Цилиндр','Конус'], correct:1, explain:'Пирамида: одно основание + треугольные боковые.' },
{ q:'Тело, полученное вращением прямоугольника вокруг одной из его сторон.', opts:['Шар','Цилиндр','Конус','Призма'], correct:1, explain:'Цилиндр — тело вращения прямоугольника.' },
{ q:'Тело, полученное вращением прямоугольного треугольника вокруг катета.', opts:['Конус','Цилиндр','Шар','Пирамида'], correct:0, explain:'Конус — вращение прямоугольного треугольника вокруг катета.' },
{ q:'Тело, полученное вращением полукруга вокруг диаметра.', opts:['Цилиндр','Конус','Шар','Тор'], correct:2, explain:'Шар — вращение полукруга вокруг диаметра.' },
{ q:'Многогранник, у которого все 4 грани — равные правильные треугольники.', opts:['Куб','Тетраэдр','Октаэдр','Икосаэдр'], correct:1, explain:'Правильный тетраэдр: 4 равносторонних треугольника.' }
];
var i1CountItems = [
{ q:'Сколько рёбер у шестиугольной призмы?', answer:'18', explain:'У $n$-угольной призмы $3n$ рёбер. При $n=6$: $3 \\cdot 6 = 18$.' },
{ q:'Сколько граней у пятиугольной пирамиды?', answer:'6', explain:'$n+1$ грань: $5+1=6$.' },
{ q:'Сколько вершин у куба?', answer:'8', explain:'У куба 8 вершин (по 4 на каждом из двух квадратных оснований).' },
{ q:'Сколько пространственных диагоналей у параллелепипеда?', answer:'4', explain:'4 диагонали — каждая соединяет противоположные вершины.' },
{ q:'Многогранник: $В=6, Г=8$. Найди $Р$ по формуле Эйлера.', answer:'12', explain:'$6 - Р + 8 = 2 \\Rightarrow Р = 12$. Это октаэдр.' },
{ q:'Сколько рёбер у 7-угольной пирамиды? (формула $2n$)', answer:'14', explain:'$2 \\cdot 7 = 14$ рёбер.' }
];
var i2AxiomItems = [
{ q:'«Через три точки $A, B, C$, не лежащие на одной прямой, проведена плоскость.»', opts:['A1','A2','A3','Следствие'], correct:0, explain:'Это формулировка A1.' },
{ q:'«Точки $M, N \\in \\alpha$, значит и вся прямая $MN \\subset \\alpha$.»', opts:['A1','A2','A3','Следствие'], correct:1, explain:'Аксиома A2.' },
{ q:'«Плоскости $\\alpha$ и $\\beta$ имеют общую точку $P$ — у них есть прямая пересечения.»', opts:['A1','A2','A3','Следствие'], correct:2, explain:'Аксиома A3.' },
{ q:'«Через прямую $a$ и точку $M \\notin a$ проходит единственная плоскость.»', opts:['A1','A2','A3','Следствие'], correct:3, explain:'Следствие из A1.' },
{ q:'«Через две пересекающиеся прямые проходит единственная плоскость.»', opts:['A1','A2','A3','Следствие'], correct:3, explain:'Следствие из A1.' },
{ q:'«Если две точки прямой лежат в плоскости — прямая лежит в плоскости.»', opts:['A1','A2','A3','Следствие'], correct:1, explain:'Точная формулировка A2.' }
];
var i2PlaneItems = [
{ q:'Даны 3 точки, не лежащие на одной прямой. Можно задать плоскость?', opts:['Да, единственную','Да, бесконечно','Нельзя'], correct:0, explain:'A1: 3 неколлинеарные точки задают единственную плоскость.' },
{ q:'Даны 3 точки на одной прямой. Сколько плоскостей через них?', opts:['Одна','Бесконечно','Ни одной'], correct:1, explain:'Плоскостей бесконечно много (вращаются вокруг прямой).' },
{ q:'Через 2 пересекающиеся прямые проходит:', opts:['Одна плоскость','Две','Бесконечно'], correct:0, explain:'Следствие: единственная.' },
{ q:'Даны 2 скрещивающиеся прямые. Можно ли провести через них одну плоскость?', opts:['Да','Нет','Иногда'], correct:1, explain:'Скрещивающиеся не лежат в одной плоскости — это и есть определение.' },
{ q:'Через прямую и точку вне её проходит:', opts:['Одна плоскость','Две','Бесконечно'], correct:0, explain:'Следствие: единственная.' }
];
var i2CountItems = [
{ q:'Сколько плоскостей через 4 точки общего положения (никакие 3 — не на одной прямой, не все в одной плоскости)?', opts:['1','3','4','6'], correct:2, explain:'Каждые 3 из 4 определяют плоскость: $C_4^3 = 4$.' },
{ q:'Через 2 параллельные прямые проходит:', opts:['1 плоскость','2','Бесконечно'], correct:0, explain:'Единственная плоскость.' },
{ q:'Через сколько точек, не лежащих на одной прямой, проходит не более 1 плоскости?', opts:['2','3','4','5'], correct:1, explain:'3 точки задают плоскость однозначно.' },
{ q:'Сколько общих точек у двух различных пересекающихся плоскостей?', opts:['Одна','Конечное число','Бесконечно (по прямой)'], correct:2, explain:'A3: пересечение по прямой ⇒ бесконечно точек.' },
{ q:'Сколько плоскостей содержат данную прямую $l$?', opts:['Одна','Две','Бесконечно'], correct:2, explain:'Через прямую — бесконечно плоскостей.' }
];
var BOSS_DEFS = {
1: {
title:'§1 — Пространственные фигуры',
xp:60,
stages:[
{ q:'Сколько рёбер у пятиугольной призмы?', type:'input', a:'15', explain:'$3n = 3 \\cdot 5 = 15$ рёбер.' },
{ q:'Сколько вершин у $n$-угольной пирамиды при $n=8$?', type:'input', a:'9', explain:'$n+1 = 9$ вершин.' },
{ q:'Какое тело получится при вращении прямоугольника вокруг стороны?', type:'mc', opts:['Шар','Цилиндр','Конус','Призма'], correct:1, explain:'Цилиндр.' },
{ q:'Сколько пространственных диагоналей у куба?', type:'input', a:'4', explain:'Куб имеет 4 пространственные диагонали.' },
{ q:'У многогранника $В=12$, $Р=30$. Сколько граней по Эйлеру?', type:'input', a:'20', explain:'$12 - 30 + Г = 2 \\Rightarrow Г = 20$. Икосаэдр.' }
]
},
2: {
title:'§2 — Прямые и плоскости',
xp:65,
stages:[
{ q:'Через сколько плоскостей проходит прямая?', type:'mc', opts:['1','2','3','Бесконечно'], correct:3, explain:'Через прямую — бесконечно плоскостей.' },
{ q:'Через 3 точки на одной прямой — сколько плоскостей?', type:'mc', opts:['1','2','3','Бесконечно'], correct:3, explain:'Точки коллинеарны ⇒ бесконечно плоскостей.' },
{ q:'Если две плоскости имеют общую точку — они пересекаются по…', type:'mc', opts:['Точке','Прямой','Плоскости','Не пересекаются'], correct:1, explain:'A3: пересечение — прямая.' },
{ 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;
var el = document.getElementById('boss-' + num);
if (!el) return;
var st = STATE.bosses[num] || { stage:0, defeated:false };
STATE.bosses[num] = st;
if (st.defeated){
el.classList.add('victory');
el.innerHTML = '<div class="boss-defeated">'
+ '<div class="boss-defeated-title">Босс §' + num + ' побеждён!</div>'
+ '<div class="boss-defeated-sub">' + def.title + '</div>'
+ '<span class="boss-defeated-xp">+' + def.xp + ' XP</span>'
+ '</div>';
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-' + num + '-in" inputmode="text" placeholder="ответ"><button id="boss-' + num + '-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(){ advanceBoss(num); }, 600);
} else {
btn.classList.add('wrong');
setTimeout(function(){ btn.classList.remove('wrong'); }, 600);
}
});
});
} else {
var inEl = document.getElementById('boss-' + num + '-in');
var goEl = document.getElementById('boss-' + num + '-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){
box.classList.remove('wrong');
inEl.style.background = 'rgba(34,197,94,.25)';
setTimeout(function(){ advanceBoss(num); }, 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 advanceBoss(num){
var st = STATE.bosses[num];
var def = BOSS_DEFS[num];
st.stage++;
if (st.stage >= def.stages.length){
st.defeated = true;
saveState();
addXp(def.xp, 'босс §' + num);
} else {
saveState();
}
renderBoss(num);
}
function tryKatex(scope){
if (!window.renderMathInElement) return;
try {
window.renderMathInElement(scope || document.body, {
delimiters: [
{left:'$$', right:'$$', display:true},
{left:'$', right:'$', display:false},
{left:'\\(', right:'\\)', display:false},
{left:'\\[', right:'\\]', display:true}
],
throwOnError: false
});
} catch(e){}
}
function start(){
buildHeroSolids();
buildAnnotatedCube();
buildPrismDirect();
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); });
document.getElementById('mark-3').addEventListener('click', function(){ markRead(3); });
refreshMarkBtn(1); refreshMarkBtn(2); refreshMarkBtn(3);
refreshTabs();
var tabs = document.querySelectorAll('.sec-tab[data-tab]');
var sections = ['para-1','para-2','para-3','para-final'];
function onScroll(){
var y = window.scrollY + 100;
var active = 0;
for (var i = 0; i < sections.length; i++){
var el = document.getElementById(sections[i]);
if (!el) continue;
if (el.offsetTop <= y) active = i;
}
tabs.forEach(function(t,j){ if (j === active) t.classList.add('active'); else t.classList.remove('active'); });
}
window.addEventListener('scroll', onScroll, {passive:true});
setTimeout(function(){ tryKatex(document.body); }, 50);
}
if (document.readyState === 'loading'){
document.addEventListener('DOMContentLoaded', start);
} else {
start();
}
</script>
</body>
</html>