From fb12196cfa799476c5e6177c6cdcae3efc04a14b Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 29 May 2026 14:46:07 +0300 Subject: [PATCH] =?UTF-8?q?feat(geom11=20ch3=20wave3):=20=C2=A77=20=C2=AB?= =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE=D0=B3=D1=80=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8=C2=BB=20=E2=80=94=205=20=D0=BF=D0=BB=D0=B0?= =?UTF-8?q?=D1=82=D0=BE=D0=BD=D0=BE=D0=B2=D1=8B=D1=85=20=D1=82=D0=B5=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/textbooks/geometry_11_ch3.html | 656 +++++++++++++++++++++++- 1 file changed, 653 insertions(+), 3 deletions(-) diff --git a/frontend/textbooks/geometry_11_ch3.html b/frontend/textbooks/geometry_11_ch3.html index 6a86835..47818e7 100644 --- a/frontend/textbooks/geometry_11_ch3.html +++ b/frontend/textbooks/geometry_11_ch3.html @@ -396,7 +396,7 @@ function buildParaSelector(){ } const BUILT=new Set(); -const BUILDERS = { p5:buildP5, p6:buildP6, p7:()=>buildStub('p7'), final3:()=>buildStub('final3') }; +const BUILDERS = { p5:buildP5, p6:buildP6, p7:buildP7, final3:()=>buildStub('final3') }; function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } } function goTo(id){ STATE.current=id; ensureBuilt(id); @@ -434,14 +434,26 @@ const SIDEBARS = { ["Куб впис. шар", "$r=a/2$"], ["Куб опис. шар", "$R=\\\\tfrac{a\\\\sqrt{3}}{2}$"] ]}, - p7:{title:"Шпаргалка § 7", rows:[["Тема", "Правильные многогранники"],["Формула","5 платоновых тел"]]}, + p7:{title:"Шпаргалка § 7", rows:[ + ["Тема", "Правильные многогранники"], + ["Условие", "грани = равные правильные $n$-угольники"], + ["Тел всего", "ровно $5$ (платоновых)"], + ["Тетраэдр", "$F{=}4,\\\\ V{=}4,\\\\ E{=}6$"], + ["Куб", "$F{=}6,\\\\ V{=}8,\\\\ E{=}12$"], + ["Октаэдр", "$F{=}8,\\\\ V{=}6,\\\\ E{=}12$"], + ["Додекаэдр", "$F{=}12,\\\\ V{=}20,\\\\ E{=}30$"], + ["Икосаэдр", "$F{=}20,\\\\ V{=}12,\\\\ E{=}30$"], + ["Эйлер", "$V-E+F=2$"], + ["Куб$\\\\leftrightarrow$Окт.", "двойственные"], + ["Дод.$\\\\leftrightarrow$Икос.", "двойственные"] + ]}, final3:{title:"Финал раздела 3", rows:[["§ 5–§ 7","теория раздела 3"],["Награда","+50 XP"]]} }; const TIPS=[ {sec:'p5',html:"Сфера: $|OM|=R$. Уравнение $(x-a)^2+(y-b)^2+(z-c)^2=R^2$. Сечение плоскостью — окружность $r=\\\\sqrt{R^2-d^2}$."}, {sec:'p6',html:"Шар: $S=4\\\\pi R^2$, $V=\\\\tfrac{4}{3}\\\\pi R^3$. Сегмент: $V=\\\\tfrac{\\\\pi h^2(3R-h)}{3}$, $S=2\\\\pi R h$. Куб впис. шар: $r=a/2$; куб опис. шар: $R=\\\\tfrac{a\\\\sqrt 3}{2}$."}, - {sec:'p7',html:"§ 7 «Правильные многогранники» — содержание в разработке. 5 платоновых тел"}, + {sec:'p7',html:"Платоновых тел ровно $5$: тетраэдр, куб, октаэдр, додекаэдр, икосаэдр. Формула Эйлера: $V-E+F=2$. Двойственные пары: куб$\\\\leftrightarrow$октаэдр, додекаэдр$\\\\leftrightarrow$икосаэдр; тетраэдр — сам себе двойственный."}, {sec:'final3',html:"Финал раздела 3 — интегрированные задачи по разделу."} ]; @@ -1437,6 +1449,640 @@ function buildP6(){ wireReadBtn('p6'); } +/* ===== § 7 «Правильные многогранники» — Wave 3 ===== */ + +/* --- Платоновы тела: вершины и рёбра для каркасного рендера --- */ +function platonicMesh(kind, a){ + // Возвращает {verts:[{x,y,z}], edges:[[i,j],...], faces:[[i0,i1,...],...]} + // Все тела отмасштабированы так, чтобы длина ребра была равна a. + const PHI = (1 + Math.sqrt(5)) / 2; + if(kind === 'tetra'){ + // (1,1,1),(1,-1,-1),(-1,1,-1),(-1,-1,1) — ребро = 2*sqrt(2) + const k = a / (2 * Math.sqrt(2)); + const v = [ + {x: 1, y: 1, z: 1}, + {x: 1, y:-1, z:-1}, + {x:-1, y: 1, z:-1}, + {x:-1, y:-1, z: 1} + ].map(p => ({x:p.x*k, y:p.y*k, z:p.z*k})); + const edges = [[0,1],[0,2],[0,3],[1,2],[1,3],[2,3]]; + const faces = [[0,1,2],[0,3,1],[0,2,3],[1,3,2]]; + return {verts:v, edges, faces}; + } + if(kind === 'cube'){ + // (±1,±1,±1), ребро = 2 + const k = a / 2; + const v = []; + for(let sx=-1; sx<=1; sx+=2) + for(let sy=-1; sy<=1; sy+=2) + for(let sz=-1; sz<=1; sz+=2) + v.push({x:sx*k, y:sy*k, z:sz*k}); + // Индексация: idx = (sx+1)/2*4 + (sy+1)/2*2 + (sz+1)/2 + // 0=(-,-,-),1=(-,-,+),2=(-,+,-),3=(-,+,+),4=(+,-,-),5=(+,-,+),6=(+,+,-),7=(+,+,+) + const edges = [ + [0,1],[0,2],[0,4],[1,3],[1,5],[2,3],[2,6],[3,7], + [4,5],[4,6],[5,7],[6,7] + ]; + const faces = [ + [0,1,3,2], [4,6,7,5], // x=-1, x=+1 + [0,4,5,1], [2,3,7,6], // y=-1, y=+1 + [0,2,6,4], [1,5,7,3] // z=-1, z=+1 + ]; + return {verts:v, edges, faces}; + } + if(kind === 'octa'){ + // (±1,0,0),(0,±1,0),(0,0,±1), ребро = sqrt(2) + const k = a / Math.sqrt(2); + const v = [ + {x: 1,y: 0,z: 0}, // 0 +x + {x:-1,y: 0,z: 0}, // 1 -x + {x: 0,y: 1,z: 0}, // 2 +y + {x: 0,y:-1,z: 0}, // 3 -y + {x: 0,y: 0,z: 1}, // 4 +z + {x: 0,y: 0,z:-1} // 5 -z + ].map(p => ({x:p.x*k, y:p.y*k, z:p.z*k})); + const edges = [ + [0,2],[0,3],[0,4],[0,5], + [1,2],[1,3],[1,4],[1,5], + [2,4],[2,5],[3,4],[3,5] + ]; + const faces = [ + [0,2,4],[0,4,3],[0,3,5],[0,5,2], + [1,4,2],[1,3,4],[1,5,3],[1,2,5] + ]; + return {verts:v, edges, faces}; + } + if(kind === 'icosa'){ + // (0,±1,±phi),(±1,±phi,0),(±phi,0,±1). Ребро = 2. + const k = a / 2; + const raw = [ + // (0,±1,±phi) + {x: 0,y: 1,z: PHI}, // 0 + {x: 0,y: 1,z:-PHI}, // 1 + {x: 0,y:-1,z: PHI}, // 2 + {x: 0,y:-1,z:-PHI}, // 3 + // (±1,±phi,0) + {x: 1,y: PHI,z: 0}, // 4 + {x: 1,y:-PHI,z: 0}, // 5 + {x:-1,y: PHI,z: 0}, // 6 + {x:-1,y:-PHI,z: 0}, // 7 + // (±phi,0,±1) + {x: PHI,y: 0,z: 1}, // 8 + {x: PHI,y: 0,z:-1}, // 9 + {x:-PHI,y: 0,z: 1}, //10 + {x:-PHI,y: 0,z:-1} //11 + ]; + const v = raw.map(p => ({x:p.x*k, y:p.y*k, z:p.z*k})); + // Рёбра — пары вершин с расстоянием = 2 (до масштабирования). Сгенерируем. + const edges = []; + const targ = 2; // длина ребра в исходных координатах + for(let i=0; i e[0]+'_'+e[1])); + function has(a,b){ return ES.has((a ({x:p.x*k, y:p.y*k, z:p.z*k})); + const edges = []; + for(let i=0; i G3D.projectIso(v, scene.cx, scene.cy, scene.scale) + : v => G3D.projectPersp(v, scene.camDist, scene.cx, scene.cy, scene.scale); + const rotated = mesh.verts.map(v => G3D.vApply(M, v)); + const proj = rotated.map(projector); + let out = ''; + // Грани (полупрозрачная заливка) — если есть + if(mesh.faces && mesh.faces.length){ + const fd = mesh.faces.map(face => { + let avgZ = 0; + for(let i=0; i= 3){ + const v0 = rotated[face[0]], v1 = rotated[face[1]], v2 = rotated[face[2]]; + const e1 = G3D.vSub(v1, v0), e2 = G3D.vSub(v2, v0); + const n = G3D.vCross(e1, e2); + visible = n.z > -1e-6; + } + return {face, avgZ, visible}; + }); + fd.sort((a,b)=>a.avgZ-b.avgZ); + for(const f of fd){ + if(!f.visible) continue; + let anyMissing = false; + const pts = []; + for(const idx of f.face){ + const p = proj[idx]; if(!p){ anyMissing=true; break; } + pts.push(p.x.toFixed(1)+','+p.y.toFixed(1)); + } + if(anyMissing) continue; + out += ''; + } + } + // Рёбра + for(const e of mesh.edges){ + const a = proj[e[0]], b = proj[e[1]]; + if(!a || !b) continue; + const za = rotated[e[0]].z, zb = rotated[e[1]].z; + const midZ = (za + zb) / 2; + const front = midZ > -1e-6; + const col = front ? strokeFront : strokeBack; + const w = front ? 2 : 1.1; + const dash = front ? '' : ' stroke-dasharray="4 3"'; + out += ''; + } + // Вершины + for(const p of proj){ + if(!p) continue; + out += ''; + } + return out; +} + +/* Свойства платоновых тел */ +const PLATONIC_INFO = { + tetra: {name:'Тетраэдр', F:4, V:4, E:6, vAtVertex:3, faceShape:'△', + volume: a => a*a*a*Math.sqrt(2)/12, + area: a => a*a*Math.sqrt(3), + volFmt: 'V = \\dfrac{a^3\\sqrt{2}}{12}', + areaFmt:'S = a^2\\sqrt{3}'}, + cube: {name:'Куб', F:6, V:8, E:12, vAtVertex:3, faceShape:'□', + volume: a => a*a*a, + area: a => 6*a*a, + volFmt: 'V = a^3', + areaFmt:'S = 6a^2'}, + octa: {name:'Октаэдр', F:8, V:6, E:12, vAtVertex:4, faceShape:'△', + volume: a => a*a*a*Math.sqrt(2)/3, + area: a => 2*a*a*Math.sqrt(3), + volFmt: 'V = \\dfrac{a^3\\sqrt{2}}{3}', + areaFmt:'S = 2a^2\\sqrt{3}'}, + dodeca: {name:'Додекаэдр', F:12, V:20, E:30, vAtVertex:3, faceShape:'⬠', + volume: a => (15 + 7*Math.sqrt(5)) * a*a*a / 4, + area: a => 3*a*a*Math.sqrt(25 + 10*Math.sqrt(5)), + volFmt: 'V = \\dfrac{(15+7\\sqrt{5})\\,a^3}{4}', + areaFmt:'S = 3a^2\\sqrt{25+10\\sqrt{5}}'}, + icosa: {name:'Икосаэдр', F:20, V:12, E:30, vAtVertex:5, faceShape:'△', + volume: a => 5 * (3 + Math.sqrt(5)) * a*a*a / 12, + area: a => 5*a*a*Math.sqrt(3), + volFmt: 'V = \\dfrac{5(3+\\sqrt{5})\\,a^3}{12}', + areaFmt:'S = 5a^2\\sqrt{3}'} +}; + +function buildP7(){ + const box = document.getElementById('p7-body'); + if(!box) return; + let html = ''; + + /* === ТЕОРИЯ === */ + + html += makeCard('theory', 'Определение и таблица 5 тел', '§ 7.1', + '

Правильный многогранник — выпуклый многогранник, у которого:

' + + '
    ' + + '
  1. Все грани — конгруэнтные правильные многоугольники.
  2. ' + + '
  3. Все двугранные углы между соседними гранями равны.
  4. ' + + '
  5. В каждой вершине сходится одинаковое число рёбер.
  6. ' + + '
' + + '

Существует ровно $5$ правильных многогранников — их называют платоновыми телами.

' + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
ТелоГрань$F$$V$$E$В вершине
Тетраэдр4463
Куб (гексаэдр)68123
Октаэдр86124
Додекаэдр1220303
Икосаэдр2012305
' + + '

Почему ровно $5$? Если в вершине сходится $k$ правильных $n$-угольников, то сумма плоских углов должна быть меньше $360^{\\circ}$. Угол правильного $n$-угольника равен $\\dfrac{180^{\\circ}(n-2)}{n}$. Условие:

' + + '

$$k\\cdot\\dfrac{180^{\\circ}(n-2)}{n} < 360^{\\circ} \\iff \\dfrac{1}{n}+\\dfrac{1}{k} > \\dfrac{1}{2}.$$

' + + '

Целочисленных решений ($n\\ge 3$, $k\\ge 3$) ровно $5$: $(n,k)\\in\\{(3,3),(3,4),(3,5),(4,3),(5,3)\\}$ — это и есть пять платоновых тел.

'); + + html += makeCard('rule', 'Формула Эйлера и двойственность', '§ 7.2', + '

Формула Эйлера для любого выпуклого многогранника:

' + + '

$$V - E + F = 2,$$

' + + '

где $V$ — число вершин, $E$ — рёбер, $F$ — граней.

' + + '

Проверка для платоновых тел:

' + + '
    ' + + '
  • Тетраэдр: $4 - 6 + 4 = 2$ ✓
  • ' + + '
  • Куб: $8 - 12 + 6 = 2$ ✓
  • ' + + '
  • Октаэдр: $6 - 12 + 8 = 2$ ✓
  • ' + + '
  • Додекаэдр: $20 - 30 + 12 = 2$ ✓
  • ' + + '
  • Икосаэдр: $12 - 30 + 20 = 2$ ✓
  • ' + + '
' + + '

Двойственные пары.

' + + '

Если в исходном теле соединить центры соседних граней — получится двойственный многогранник. Числа $F$ и $V$ у двойственной пары меняются местами, число рёбер $E$ сохраняется.

' + + '
    ' + + '
  • Тетраэдр $\\leftrightarrow$ тетраэдравтодуальное тело: центрам граней тетраэдра соответствуют вершины другого тетраэдра.
  • ' + + '
  • Куб $\\leftrightarrow$ октаэдр: $8$ вершин куба $\\leftrightarrow$ $8$ граней октаэдра; рёбер у обоих по $12$.
  • ' + + '
  • Додекаэдр $\\leftrightarrow$ икосаэдр: $20$ вершин $\\leftrightarrow$ $20$ граней; рёбер по $30$.
  • ' + + '
' + + '
Пример: куб $\\to$ октаэдр
' + + '

Соединим центры $6$ граней куба — получим $6$ вершин нового тела. Каждая пара соседних граней даёт ребро ($12$ пар $\\Rightarrow 12$ рёбер). Грани нового тела — треугольники вокруг каждой вершины куба ($8$ вершин $\\Rightarrow 8$ граней). Это октаэдр.

' + + '
'); + + html += makeCard('example', 'Формулы объёмов и применение', '§ 7.3', + '

Объёмы и площади для длины ребра $a$:

' + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
ТелоОбъём $V$Площадь $S$
Тетраэдр$\\dfrac{a^3\\sqrt{2}}{12}$$a^2\\sqrt{3}$
Куб$a^3$$6a^2$
Октаэдр$\\dfrac{a^3\\sqrt{2}}{3}$$2a^2\\sqrt{3}$
Додекаэдр$\\dfrac{(15+7\\sqrt{5})\\,a^3}{4}$$3a^2\\sqrt{25+10\\sqrt{5}}$
Икосаэдр$\\dfrac{5(3+\\sqrt{5})\\,a^3}{12}$$5a^2\\sqrt{3}$
' + + '

Применение в природе и культуре.

' + + '
    ' + + '
  • Кристаллы: пирит образует кубические кристаллы, флюорит — октаэдрические, гранат — додекаэдрические.
  • ' + + '
  • Вирусы: капсиды многих вирусов (включая ВИЧ, аденовирусы) имеют форму икосаэдра.
  • ' + + '
  • Математика и философия: Пифагорейцы и Платон связывали $5$ тел с пятью «стихиями»: куб — земля, тетраэдр — огонь, октаэдр — воздух, икосаэдр — вода, додекаэдр — эфир (космос).
  • ' + + '
  • Игральные кости: D4, D6, D8, D12, D20 — ровно по одной кости каждой формы платонова тела.
  • ' + + '
' + + '
Пример: куб со стороной $a=4$
' + + '

$V = a^3 = 64$.

' + + '

$S = 6a^2 = 96$.

' + + '
' + + '
Пример: октаэдр со стороной $a=3$
' + + '

$V = \\dfrac{a^3\\sqrt{2}}{3} = \\dfrac{27\\sqrt{2}}{3} = 9\\sqrt{2} \\approx 12{,}73$.

' + + '

$S = 2a^2\\sqrt{3} = 18\\sqrt{3} \\approx 31{,}18$.

' + + '
'); + + /* === ИНТЕРАКТИВ 1 — 3D-визуализатор 5 платоновых тел === */ + html += '
' + + '
3D \xb7 платоновы тела
Визуализатор $5$ правильных многогранников
' + + '
Переключай тела, меняй ребро $a$, вращай мышью. После просмотра всех $5$ тел — +10 XP.
' + + '
' + + '' + + '' + + '' + + '' + + '' + + '
' + + '
' + + '' + + '
' + + '
' + + '' + + '' + + '' + + '' + + '
' + + '
' + + '
' + + '
Изучено тел: 0 / 5
' + + '
'; + + /* === ИНТЕРАКТИВ 2 — Калькулятор V и S === */ + html += '
' + + '
калькулятор
$V$ и $S$ платоновых тел
' + + '
Выбери тело, введи длину ребра $a$ — получи объём и площадь поверхности с подстановкой.
' + + '
' + + '' + + '$a=$' + + '' + + '
' + + '
' + + '
'; + + /* === ИНТЕРАКТИВ 3 — Двойственные пары (DnD) === */ + html += '
' + + '
DnD \xb7 двойственность
Двойственные пары платоновых тел
' + + '
Перетащи $6$ карточек по $3$ ящикам так, чтобы в каждом ящике стояла правильная двойственная пара. После проверки — +15 XP.
' + + '
' + + '
' + + '
Пара 1
' + + '
Пара 2
' + + '
Пара 3
' + + '
' + + '
' + + '' + + '
'; + + /* === ИНТЕРАКТИВ 4 — Тренажёр === */ + html += '
' + + '
тренажёр \xb7 6 задач
Грани, вершины, рёбра и объёмы
' + + '
Введи числовой ответ. Допуск $\\pm 0{,}05$ для дробных значений.
' + + '
' + + '
Решено: 0 / 6
' + + '
'; + + html += secNav('p6', 'final3'); + html += readButton('p7'); + + box.innerHTML = html; + renderMath(box); + + /* ====== JS-логика интерактивов ====== */ + + /* IV1 — 3D-визуализатор */ + (function(){ + if(!window.G3D) return; + const svg = document.getElementById('p7-iv1-svg'); + const elA = document.getElementById('p7-iv1-a'); + const vA = document.getElementById('p7-iv1-a-v'); + const info = document.getElementById('p7-iv1-info'); + const oCnt = document.getElementById('p7-iv1-cnt'); + if(!svg) return; + const scene = G3D.createScene({W:480, H:400, scale:60, camDist:8, rotX:-0.35, rotY:0.7}); + let curKind = 'tetra'; + const seen = new Set(); + let xpGiven = false; + const KINDS = ['tetra','cube','octa','dodeca','icosa']; + + function setActiveBtn(){ + KINDS.forEach(function(k){ + const b = document.getElementById('p7-iv1-' + k); + if(b) b.classList.toggle('primary', k === curKind); + }); + } + + function draw(){ + const a = +elA.value; + vA.textContent = a.toFixed(1); + const mesh = platonicMesh(curKind, a); + const M = G3D.buildRotMatrix(scene); + svg.innerHTML = renderWireframe(mesh, M, scene); + const inf = PLATONIC_INFO[curKind]; + const Vnum = inf.volume(a); + const Snum = inf.area(a); + info.innerHTML = '

' + inf.name + '

' + + '
' + + 'Граней $F=$' + inf.F + '' + + 'Вершин $V=$' + inf.V + '' + + 'Рёбер $E=$' + inf.E + '' + + 'В вершине рёбер: ' + inf.vAtVertex + '' + + '
' + + '

$' + inf.volFmt + '\\approx ' + fmt(+Vnum.toFixed(3)) + '$ (для $a=' + fmt(a) + '$)

' + + '

$' + inf.areaFmt + '\\approx ' + fmt(+Snum.toFixed(3)) + '$

' + + '

Проверка Эйлера: $V-E+F=' + inf.V + '-' + inf.E + '+' + inf.F + '=' + (inf.V - inf.E + inf.F) + '$.

'; + renderMath(info); + seen.add(curKind); + oCnt.textContent = seen.size; + if(seen.size >= 5 && !xpGiven){ + xpGiven = true; + addXp(10, 'p7-iv1'); + bumpProgress('p7', 15); + const note = document.createElement('div'); + note.className = 'feedback ok'; + note.innerHTML = '✓ +10 XP за изучение всех $5$ платоновых тел!'; + note.style.cssText = 'display:block;margin-top:8px'; + const host = document.getElementById('p7-iv1'); + if(host) host.appendChild(note); + try{ renderMath(note); }catch(e){} + setTimeout(function(){ try{ note.remove(); }catch(e){} }, 3000); + } + } + setActiveBtn(); draw(); + G3D.attachOrbit(svg, scene, draw); + elA.addEventListener('input', draw); + KINDS.forEach(function(k){ + const b = document.getElementById('p7-iv1-' + k); + if(b) b.addEventListener('click', function(){ curKind = k; setActiveBtn(); draw(); }); + }); + document.querySelectorAll('#p7-iv1 .g3d-tools .btn').forEach(function(b){ + b.addEventListener('click', function(){ G3D.presetView(scene, b.dataset.view, draw); }); + }); + })(); + + /* IV2 — Калькулятор */ + (function(){ + const sel = document.getElementById('p7-iv2-sel'); + const elA = document.getElementById('p7-iv2-a'); + const outBox = document.getElementById('p7-iv2-out'); + const btn = document.getElementById('p7-iv2-calc'); + let xpGiven = false; + const seenKinds = new Set(); + + function parseNum(id){ const el = document.getElementById(id); if(!el) return NaN; const v = (el.value||'').replace(',', '.').trim(); const x = parseFloat(v); return isFinite(x) ? x : NaN; } + + btn.addEventListener('click', function(){ + const kind = sel.value; + const a = parseNum('p7-iv2-a'); + if(!isFinite(a) || a <= 0){ + outBox.innerHTML = '✗ Введи $a>0$.'; + renderMath(outBox); + return; + } + const inf = PLATONIC_INFO[kind]; + const V = inf.volume(a); + const S = inf.area(a); + outBox.innerHTML = '

' + inf.name + ' с длиной ребра $a=' + fmt(a) + '$:

' + + '

$' + inf.volFmt + ' \\approx ' + fmt(+V.toFixed(4)) + '$

' + + '

$' + inf.areaFmt + ' \\approx ' + fmt(+S.toFixed(4)) + '$

' + + '

Граней: $F=' + inf.F + '$, вершин: $V=' + inf.V + '$, рёбер: $E=' + inf.E + '$.

'; + renderMath(outBox); + seenKinds.add(kind); + if(seenKinds.size >= 3 && !xpGiven){ + xpGiven = true; + addXp(10, 'p7-iv2'); + bumpProgress('p7', 15); + } + }); + })(); + + /* IV3 — Двойственные пары (DnD) */ + (function(){ + // 6 карточек, 3 пары: t1+t2 (тетраэдр), c+o (куб/октаэдр), d+i (додекаэдр/икосаэдр). + // Правильное распределение по ящикам: pair1={t1,t2}, pair2={c,o}, pair3={d,i}. + const items = [ + { id:'t1', html:'Тетраэдр', cat:'pair1' }, + { id:'t2', html:'Тетраэдр (двойственный)', cat:'pair1' }, + { id:'c', html:'Куб', cat:'pair2' }, + { id:'o', html:'Октаэдр', cat:'pair2' }, + { id:'d', html:'Додекаэдр', cat:'pair3' }, + { id:'i', html:'Икосаэдр', cat:'pair3' } + ]; + // Поскольку пары симметричны (любой ящик может быть любой парой), проверяем по структуре, а не по cat. + const REAL_PAIRS = { + tetra: new Set(['t1','t2']), + cube_octa: new Set(['c','o']), + dodeca_icosa: new Set(['d','i']) + }; + const sorter = setupSorter({ + poolId:'p7-iv3-pool', + scopeSelector:'#p7-iv3', + items: items, + cats: ['pair1','pair2','pair3'] + }); + let xpGiven = false; + document.getElementById('p7-iv3-check').addEventListener('click', function(){ + const fb = document.getElementById('p7-iv3-fb'); + // Группируем по cat + const byCat = {pair1:[], pair2:[], pair3:[]}; + let allPlaced = true; + items.forEach(function(it){ + const c = sorter.placed[it.id]; + if(!c){ allPlaced = false; return; } + byCat[c].push(it.id); + }); + if(!allPlaced){ + feedback(fb, false, '✗ Распредели все $6$ карточек по ящикам.'); + return; + } + // Проверяем, что каждая группа из двух элементов соответствует одной из настоящих пар + const groups = ['pair1','pair2','pair3'].map(function(c){ return new Set(byCat[c]); }); + function eqSet(a, b){ + if(a.size !== b.size) return false; + for(const x of a) if(!b.has(x)) return false; + return true; + } + const realSets = [REAL_PAIRS.tetra, REAL_PAIRS.cube_octa, REAL_PAIRS.dodeca_icosa]; + // Каждая группа должна совпасть с одной из настоящих пар, без повторов + const used = new Set(); + let ok = true; + for(const g of groups){ + if(g.size !== 2){ ok = false; break; } + let found = -1; + for(let r = 0; r < realSets.length; r++){ + if(used.has(r)) continue; + if(eqSet(g, realSets[r])){ found = r; break; } + } + if(found < 0){ ok = false; break; } + used.add(found); + } + if(ok){ + feedback(fb, true, '✓ Верно! Двойственные пары: тетраэдр $\\leftrightarrow$ тетраэдр, куб $\\leftrightarrow$ октаэдр, додекаэдр $\\leftrightarrow$ икосаэдр.'); + if(!xpGiven){ + xpGiven = true; + addXp(15, 'p7-iv3'); + bumpProgress('p7', 25); + } + } else { + feedback(fb, false, '✗ В каждом ящике должна стоять одна двойственная пара: тетраэдр+тетраэдр, куб+октаэдр, додекаэдр+икосаэдр.'); + } + }); + document.getElementById('p7-iv3-reset').addEventListener('click', function(){ + sorter.reset(); + const fb = document.getElementById('p7-iv3-fb'); + if(fb){ fb.style.display = 'none'; fb.innerHTML = ''; } + }); + })(); + + /* IV4 — Тренажёр */ + (function(){ + const tasks = [ + { q:'Сколько граней у икосаэдра?', a:20, tol:0.05 }, + { q:'Сколько вершин у додекаэдра?', a:20, tol:0.05 }, + { q:'Сколько рёбер у куба?', a:12, tol:0.05 }, + { q:'Куб со стороной $a=3$. Найди $V$.', a:27, tol:0.05 }, + { q:'Тетраэдр со стороной $a=2$. Найди $V$ (с точностью до $0{,}01$).', a:0.94, tol:0.05 }, + { q:'Октаэдр со стороной $a=3$. Найди $V$ (с точностью до $0{,}01$).', a:12.73, tol:0.05 } + ]; + const list = document.getElementById('p7-iv4-list'); + const scoreEl = document.getElementById('p7-iv4-score'); + const solved = new Set(); + let xpGiven = false; + list.innerHTML = tasks.map(function(t, i){ + return '
' + + '
Задача ' + (i+1) + '. ' + t.q + '
' + + '
' + + '' + + '' + + '
' + + '' + + '
'; + }).join(''); + renderMath(list); + list.querySelectorAll('button[data-i]').forEach(function(b){ + b.addEventListener('click', function(){ + const i = +b.dataset.i, t = tasks[i]; + const inp = document.getElementById('p7-iv4-inp-' + i); + const fb = document.getElementById('p7-iv4-fb-' + i); + const raw = (inp.value || '').replace(',', '.').trim(); + const val = parseFloat(raw); + if(!isFinite(val)){ feedback(fb, false, '✗ Введи число'); return; } + if(Math.abs(val - t.a) <= t.tol){ + feedback(fb, true, '✓ Верно!'); + if(!solved.has(i)){ + solved.add(i); + scoreEl.textContent = solved.size; + if(solved.size === tasks.length && !xpGiven){ + xpGiven = true; + addXp(15, 'p7-iv4'); + bumpProgress('p7', 25); + setTimeout(function(){ achievement('p7_done'); }, 400); + } + } + } else { + feedback(fb, false, '✗ Не точно. Пересчитай аккуратно.'); + } + }); + }); + })(); + + wireReadBtn('p7'); +} + /* ===== STUB BUILDER — единый для всех параграфов раздела (Phase 0) ===== */ function buildStub(id){ @@ -1527,6 +2173,10 @@ const SEARCH_INDEX = (function(){ arr.push({kind:'Теория',title:'Шаровой сектор и слой',desc:'V_сект = (2/3)πR²h; V_слой = πh(3r1²+3r2²+h²)/6',sec:'p6'}); arr.push({kind:'Теория',title:'Шар, вписанный и описанный около куба',desc:'r = a/2, R = a√3/2',sec:'p6'}); arr.push({kind:'Теория',title:'Шар и цилиндр',desc:'вписан: h=2R; описан: R_шар=√(R²+(h/2)²)',sec:'p6'}); + arr.push({kind:'Теория',title:'Правильные многогранники',desc:'тетраэдр, куб, октаэдр, додекаэдр, икосаэдр — 5 платоновых тел',sec:'p7'}); + arr.push({kind:'Теория',title:'Формула Эйлера',desc:'V − E + F = 2 для выпуклых многогранников',sec:'p7'}); + arr.push({kind:'Теория',title:'Двойственные платоновы тела',desc:'куб ↔ октаэдр, додекаэдр ↔ икосаэдр, тетраэдр ↔ тетраэдр',sec:'p7'}); + arr.push({kind:'Теория',title:'Объёмы платоновых тел',desc:'тетраэдр a³√2/12, куб a³, октаэдр a³√2/3, икосаэдр 5(3+√5)a³/12',sec:'p7'}); return arr; })(); function initSearch(){