feat(geom11 ch3 wave3): §7 «Правильные многогранники» — 5 платоновых тел
This commit is contained in:
@@ -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<raw.length; i++){
|
||||
for(let j=i+1; j<raw.length; j++){
|
||||
const dx=raw[i].x-raw[j].x, dy=raw[i].y-raw[j].y, dz=raw[i].z-raw[j].z;
|
||||
const d = Math.sqrt(dx*dx+dy*dy+dz*dz);
|
||||
if(Math.abs(d - targ) < 1e-3) edges.push([i,j]);
|
||||
}
|
||||
}
|
||||
// Грани — треугольники (i,j,k), где все три пары — рёбра.
|
||||
const ES = new Set(edges.map(e => e[0]+'_'+e[1]));
|
||||
function has(a,b){ return ES.has((a<b?a+'_'+b:b+'_'+a)); }
|
||||
const faces = [];
|
||||
for(let i=0; i<raw.length; i++){
|
||||
for(let j=i+1; j<raw.length; j++){
|
||||
if(!has(i,j)) continue;
|
||||
for(let k2=j+1; k2<raw.length; k2++){
|
||||
if(has(i,k2) && has(j,k2)) faces.push([i,j,k2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {verts:v, edges, faces};
|
||||
}
|
||||
if(kind === 'dodeca'){
|
||||
// Вершины: (±1,±1,±1), (0,±1/phi,±phi), (±1/phi,±phi,0), (±phi,0,±1/phi).
|
||||
// Длина ребра = 2/phi.
|
||||
const INV = 1 / PHI;
|
||||
const raw = [];
|
||||
// куб (±1,±1,±1) → 8
|
||||
for(let sx=-1; sx<=1; sx+=2)
|
||||
for(let sy=-1; sy<=1; sy+=2)
|
||||
for(let sz=-1; sz<=1; sz+=2)
|
||||
raw.push({x:sx, y:sy, z:sz});
|
||||
// (0,±1/phi,±phi) → 4
|
||||
for(let sy=-1; sy<=1; sy+=2)
|
||||
for(let sz=-1; sz<=1; sz+=2)
|
||||
raw.push({x:0, y:sy*INV, z:sz*PHI});
|
||||
// (±1/phi,±phi,0) → 4
|
||||
for(let sx=-1; sx<=1; sx+=2)
|
||||
for(let sy=-1; sy<=1; sy+=2)
|
||||
raw.push({x:sx*INV, y:sy*PHI, z:0});
|
||||
// (±phi,0,±1/phi) → 4
|
||||
for(let sx=-1; sx<=1; sx+=2)
|
||||
for(let sz=-1; sz<=1; sz+=2)
|
||||
raw.push({x:sx*PHI, y:0, z:sz*INV});
|
||||
const targ = 2 / PHI;
|
||||
const k = a / targ;
|
||||
const v = raw.map(p => ({x:p.x*k, y:p.y*k, z:p.z*k}));
|
||||
const edges = [];
|
||||
for(let i=0; i<raw.length; i++){
|
||||
for(let j=i+1; j<raw.length; j++){
|
||||
const dx=raw[i].x-raw[j].x, dy=raw[i].y-raw[j].y, dz=raw[i].z-raw[j].z;
|
||||
const d = Math.sqrt(dx*dx+dy*dy+dz*dz);
|
||||
if(Math.abs(d - targ) < 1e-3) edges.push([i,j]);
|
||||
}
|
||||
}
|
||||
return {verts:v, edges, faces:[]};
|
||||
}
|
||||
return {verts:[], edges:[], faces:[]};
|
||||
}
|
||||
|
||||
/* Рендер каркаса по {verts,edges} через G3D */
|
||||
function renderWireframe(mesh, M, scene, opts){
|
||||
opts = opts || {};
|
||||
const strokeFront = opts.strokeFront || '#7c3aed';
|
||||
const strokeBack = opts.strokeBack || '#c4b5fd';
|
||||
const projector = (scene.proj === 'iso')
|
||||
? v => 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<face.length; i++) avgZ += rotated[face[i]].z;
|
||||
avgZ /= face.length;
|
||||
// нормаль — по первым трём вершинам
|
||||
let visible = true;
|
||||
if(face.length >= 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 += '<polygon points="'+pts.join(' ')+'" fill="rgba(237,233,254,.45)" stroke="none"/>';
|
||||
}
|
||||
}
|
||||
// Рёбра
|
||||
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 += '<line x1="'+a.x.toFixed(1)+'" y1="'+a.y.toFixed(1)+'" x2="'+b.x.toFixed(1)+'" y2="'+b.y.toFixed(1)+'" stroke="'+col+'" stroke-width="'+w+'" stroke-linecap="round" opacity="'+(front?'1':'.55')+'"'+dash+'/>';
|
||||
}
|
||||
// Вершины
|
||||
for(const p of proj){
|
||||
if(!p) continue;
|
||||
out += '<circle cx="'+p.x.toFixed(1)+'" cy="'+p.y.toFixed(1)+'" r="2.5" fill="#3b0764"/>';
|
||||
}
|
||||
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',
|
||||
'<p><b>Правильный многогранник</b> — выпуклый многогранник, у которого:</p>'
|
||||
+ '<ol style="margin:6px 0 10px 22px;line-height:1.7">'
|
||||
+ '<li>Все грани — <b>конгруэнтные правильные многоугольники</b>.</li>'
|
||||
+ '<li>Все двугранные углы между соседними гранями равны.</li>'
|
||||
+ '<li>В каждой вершине сходится одинаковое число рёбер.</li>'
|
||||
+ '</ol>'
|
||||
+ '<p style="background:var(--sec-acc-soft,var(--pri-soft));border-left:4px solid var(--sec-acc,var(--pri));padding:8px 12px;border-radius:6px;margin:8px 0"><b>Существует ровно $5$ правильных многогранников</b> — их называют <b>платоновыми телами</b>.</p>'
|
||||
+ '<div style="overflow-x:auto;margin:10px 0"><table style="width:100%;border-collapse:collapse;font-size:.92rem">'
|
||||
+ '<thead><tr style="background:var(--sec-acc-soft,var(--pri-soft));color:var(--sec-acc-d,var(--pri2))">'
|
||||
+ '<th style="padding:8px 10px;text-align:left;border-bottom:2px solid var(--sec-acc,var(--pri))">Тело</th>'
|
||||
+ '<th style="padding:8px 10px;text-align:center;border-bottom:2px solid var(--sec-acc,var(--pri))">Грань</th>'
|
||||
+ '<th style="padding:8px 10px;text-align:center;border-bottom:2px solid var(--sec-acc,var(--pri))">$F$</th>'
|
||||
+ '<th style="padding:8px 10px;text-align:center;border-bottom:2px solid var(--sec-acc,var(--pri))">$V$</th>'
|
||||
+ '<th style="padding:8px 10px;text-align:center;border-bottom:2px solid var(--sec-acc,var(--pri))">$E$</th>'
|
||||
+ '<th style="padding:8px 10px;text-align:center;border-bottom:2px solid var(--sec-acc,var(--pri))">В вершине</th>'
|
||||
+ '</tr></thead><tbody>'
|
||||
+ '<tr><td style="padding:7px 10px;border-bottom:1px solid var(--border)"><b>Тетраэдр</b></td><td style="text-align:center">△</td><td style="text-align:center">4</td><td style="text-align:center">4</td><td style="text-align:center">6</td><td style="text-align:center">3</td></tr>'
|
||||
+ '<tr><td style="padding:7px 10px;border-bottom:1px solid var(--border)"><b>Куб (гексаэдр)</b></td><td style="text-align:center">□</td><td style="text-align:center">6</td><td style="text-align:center">8</td><td style="text-align:center">12</td><td style="text-align:center">3</td></tr>'
|
||||
+ '<tr><td style="padding:7px 10px;border-bottom:1px solid var(--border)"><b>Октаэдр</b></td><td style="text-align:center">△</td><td style="text-align:center">8</td><td style="text-align:center">6</td><td style="text-align:center">12</td><td style="text-align:center">4</td></tr>'
|
||||
+ '<tr><td style="padding:7px 10px;border-bottom:1px solid var(--border)"><b>Додекаэдр</b></td><td style="text-align:center">⬠</td><td style="text-align:center">12</td><td style="text-align:center">20</td><td style="text-align:center">30</td><td style="text-align:center">3</td></tr>'
|
||||
+ '<tr><td style="padding:7px 10px"><b>Икосаэдр</b></td><td style="text-align:center">△</td><td style="text-align:center">20</td><td style="text-align:center">12</td><td style="text-align:center">30</td><td style="text-align:center">5</td></tr>'
|
||||
+ '</tbody></table></div>'
|
||||
+ '<p><b>Почему ровно $5$?</b> Если в вершине сходится $k$ правильных $n$-угольников, то сумма плоских углов должна быть меньше $360^{\\circ}$. Угол правильного $n$-угольника равен $\\dfrac{180^{\\circ}(n-2)}{n}$. Условие:</p>'
|
||||
+ '<p style="text-align:center;margin:8px 0">$$k\\cdot\\dfrac{180^{\\circ}(n-2)}{n} < 360^{\\circ} \\iff \\dfrac{1}{n}+\\dfrac{1}{k} > \\dfrac{1}{2}.$$</p>'
|
||||
+ '<p>Целочисленных решений ($n\\ge 3$, $k\\ge 3$) ровно $5$: $(n,k)\\in\\{(3,3),(3,4),(3,5),(4,3),(5,3)\\}$ — это и есть пять платоновых тел.</p>');
|
||||
|
||||
html += makeCard('rule', 'Формула Эйлера и двойственность', '§ 7.2',
|
||||
'<p><b>Формула Эйлера</b> для любого выпуклого многогранника:</p>'
|
||||
+ '<p style="text-align:center;margin:8px 0">$$V - E + F = 2,$$</p>'
|
||||
+ '<p>где $V$ — число вершин, $E$ — рёбер, $F$ — граней.</p>'
|
||||
+ '<p><b>Проверка для платоновых тел:</b></p>'
|
||||
+ '<ul style="margin:6px 0 10px 22px;line-height:1.7">'
|
||||
+ '<li>Тетраэдр: $4 - 6 + 4 = 2$ ✓</li>'
|
||||
+ '<li>Куб: $8 - 12 + 6 = 2$ ✓</li>'
|
||||
+ '<li>Октаэдр: $6 - 12 + 8 = 2$ ✓</li>'
|
||||
+ '<li>Додекаэдр: $20 - 30 + 12 = 2$ ✓</li>'
|
||||
+ '<li>Икосаэдр: $12 - 30 + 20 = 2$ ✓</li>'
|
||||
+ '</ul>'
|
||||
+ '<p style="font-weight:700;margin-top:10px">Двойственные пары.</p>'
|
||||
+ '<p>Если в исходном теле соединить центры соседних граней — получится <b>двойственный</b> многогранник. Числа $F$ и $V$ у двойственной пары меняются местами, число рёбер $E$ сохраняется.</p>'
|
||||
+ '<ul style="margin:6px 0 10px 22px;line-height:1.7">'
|
||||
+ '<li><b>Тетраэдр $\\leftrightarrow$ тетраэдр</b> — <i>автодуальное</i> тело: центрам граней тетраэдра соответствуют вершины другого тетраэдра.</li>'
|
||||
+ '<li><b>Куб $\\leftrightarrow$ октаэдр</b>: $8$ вершин куба $\\leftrightarrow$ $8$ граней октаэдра; рёбер у обоих по $12$.</li>'
|
||||
+ '<li><b>Додекаэдр $\\leftrightarrow$ икосаэдр</b>: $20$ вершин $\\leftrightarrow$ $20$ граней; рёбер по $30$.</li>'
|
||||
+ '</ul>'
|
||||
+ '<details class="spoiler"><summary>Пример: куб $\\to$ октаэдр</summary><div class="spoiler-body">'
|
||||
+ '<p>Соединим центры $6$ граней куба — получим $6$ вершин нового тела. Каждая пара соседних граней даёт ребро ($12$ пар $\\Rightarrow 12$ рёбер). Грани нового тела — треугольники вокруг каждой вершины куба ($8$ вершин $\\Rightarrow 8$ граней). Это <b>октаэдр</b>.</p>'
|
||||
+ '</div></details>');
|
||||
|
||||
html += makeCard('example', 'Формулы объёмов и применение', '§ 7.3',
|
||||
'<p><b>Объёмы и площади</b> для длины ребра $a$:</p>'
|
||||
+ '<div style="overflow-x:auto;margin:8px 0"><table style="width:100%;border-collapse:collapse;font-size:.92rem">'
|
||||
+ '<thead><tr style="background:var(--sec-acc-soft,var(--pri-soft));color:var(--sec-acc-d,var(--pri2))">'
|
||||
+ '<th style="padding:8px 10px;text-align:left;border-bottom:2px solid var(--sec-acc,var(--pri))">Тело</th>'
|
||||
+ '<th style="padding:8px 10px;text-align:center;border-bottom:2px solid var(--sec-acc,var(--pri))">Объём $V$</th>'
|
||||
+ '<th style="padding:8px 10px;text-align:center;border-bottom:2px solid var(--sec-acc,var(--pri))">Площадь $S$</th>'
|
||||
+ '</tr></thead><tbody>'
|
||||
+ '<tr><td style="padding:7px 10px;border-bottom:1px solid var(--border)"><b>Тетраэдр</b></td><td style="text-align:center">$\\dfrac{a^3\\sqrt{2}}{12}$</td><td style="text-align:center">$a^2\\sqrt{3}$</td></tr>'
|
||||
+ '<tr><td style="padding:7px 10px;border-bottom:1px solid var(--border)"><b>Куб</b></td><td style="text-align:center">$a^3$</td><td style="text-align:center">$6a^2$</td></tr>'
|
||||
+ '<tr><td style="padding:7px 10px;border-bottom:1px solid var(--border)"><b>Октаэдр</b></td><td style="text-align:center">$\\dfrac{a^3\\sqrt{2}}{3}$</td><td style="text-align:center">$2a^2\\sqrt{3}$</td></tr>'
|
||||
+ '<tr><td style="padding:7px 10px;border-bottom:1px solid var(--border)"><b>Додекаэдр</b></td><td style="text-align:center">$\\dfrac{(15+7\\sqrt{5})\\,a^3}{4}$</td><td style="text-align:center">$3a^2\\sqrt{25+10\\sqrt{5}}$</td></tr>'
|
||||
+ '<tr><td style="padding:7px 10px"><b>Икосаэдр</b></td><td style="text-align:center">$\\dfrac{5(3+\\sqrt{5})\\,a^3}{12}$</td><td style="text-align:center">$5a^2\\sqrt{3}$</td></tr>'
|
||||
+ '</tbody></table></div>'
|
||||
+ '<p style="font-weight:700;margin-top:8px">Применение в природе и культуре.</p>'
|
||||
+ '<ul style="margin:6px 0 10px 22px;line-height:1.7">'
|
||||
+ '<li><b>Кристаллы:</b> пирит образует кубические кристаллы, флюорит — октаэдрические, гранат — додекаэдрические.</li>'
|
||||
+ '<li><b>Вирусы:</b> капсиды многих вирусов (включая ВИЧ, аденовирусы) имеют форму икосаэдра.</li>'
|
||||
+ '<li><b>Математика и философия:</b> Пифагорейцы и Платон связывали $5$ тел с пятью «стихиями»: куб — земля, тетраэдр — огонь, октаэдр — воздух, икосаэдр — вода, додекаэдр — эфир (космос).</li>'
|
||||
+ '<li><b>Игральные кости:</b> D4, D6, D8, D12, D20 — ровно по одной кости каждой формы платонова тела.</li>'
|
||||
+ '</ul>'
|
||||
+ '<details class="spoiler"><summary>Пример: куб со стороной $a=4$</summary><div class="spoiler-body">'
|
||||
+ '<p>$V = a^3 = 64$.</p>'
|
||||
+ '<p>$S = 6a^2 = 96$.</p>'
|
||||
+ '</div></details>'
|
||||
+ '<details class="spoiler"><summary>Пример: октаэдр со стороной $a=3$</summary><div class="spoiler-body">'
|
||||
+ '<p>$V = \\dfrac{a^3\\sqrt{2}}{3} = \\dfrac{27\\sqrt{2}}{3} = 9\\sqrt{2} \\approx 12{,}73$.</p>'
|
||||
+ '<p>$S = 2a^2\\sqrt{3} = 18\\sqrt{3} \\approx 31{,}18$.</p>'
|
||||
+ '</div></details>');
|
||||
|
||||
/* === ИНТЕРАКТИВ 1 — 3D-визуализатор 5 платоновых тел === */
|
||||
html += '<div class="wg" id="p7-iv1">'
|
||||
+ '<div class="wg-header"><span class="wg-badge">3D \xb7 платоновы тела</span><div class="wg-title">Визуализатор $5$ правильных многогранников</div></div>'
|
||||
+ '<div class="wg-help">Переключай тела, меняй ребро $a$, вращай мышью. После просмотра <b>всех $5$ тел</b> — +10 XP.</div>'
|
||||
+ '<div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px">'
|
||||
+ '<button class="btn primary" data-pt="tetra" id="p7-iv1-tetra">Тетраэдр</button>'
|
||||
+ '<button class="btn" data-pt="cube" id="p7-iv1-cube">Куб</button>'
|
||||
+ '<button class="btn" data-pt="octa" id="p7-iv1-octa">Октаэдр</button>'
|
||||
+ '<button class="btn" data-pt="dodeca" id="p7-iv1-dodeca">Додекаэдр</button>'
|
||||
+ '<button class="btn" data-pt="icosa" id="p7-iv1-icosa">Икосаэдр</button>'
|
||||
+ '</div>'
|
||||
+ '<div class="sliders">'
|
||||
+ '<label>$a$ (ребро):<b id="p7-iv1-a-v">2.0</b><input type="range" id="p7-iv1-a" min="1" max="4" step="0.1" value="2"></label>'
|
||||
+ '</div>'
|
||||
+ '<div class="g3d-tools">'
|
||||
+ '<button class="btn" data-view="iso">Изо</button>'
|
||||
+ '<button class="btn" data-view="front">Спереди</button>'
|
||||
+ '<button class="btn" data-view="top">Сверху</button>'
|
||||
+ '<button class="btn" data-view="side">Сбоку</button>'
|
||||
+ '</div>'
|
||||
+ '<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px;text-align:center"><svg id="p7-iv1-svg" viewBox="0 0 480 400" width="100%" style="max-width:480px;height:auto"></svg></div>'
|
||||
+ '<div id="p7-iv1-info" style="margin-top:10px;background:var(--card);border:1px solid var(--border);border-radius:9px;padding:10px 12px;font-size:.93rem;line-height:1.65"></div>'
|
||||
+ '<div style="font-size:.78rem;color:var(--muted);margin-top:6px">Изучено тел: <b id="p7-iv1-cnt">0</b> / 5</div>'
|
||||
+ '</div>';
|
||||
|
||||
/* === ИНТЕРАКТИВ 2 — Калькулятор V и S === */
|
||||
html += '<div class="wg" id="p7-iv2">'
|
||||
+ '<div class="wg-header"><span class="wg-badge">калькулятор</span><div class="wg-title">$V$ и $S$ платоновых тел</div></div>'
|
||||
+ '<div class="wg-help">Выбери тело, введи длину ребра $a$ — получи объём и площадь поверхности с подстановкой.</div>'
|
||||
+ '<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-bottom:10px">'
|
||||
+ '<select id="p7-iv2-sel" class="tinp" style="padding:6px 10px">'
|
||||
+ '<option value="tetra">Тетраэдр</option>'
|
||||
+ '<option value="cube" selected>Куб</option>'
|
||||
+ '<option value="octa">Октаэдр</option>'
|
||||
+ '<option value="dodeca">Додекаэдр</option>'
|
||||
+ '<option value="icosa">Икосаэдр</option>'
|
||||
+ '</select>'
|
||||
+ '<span>$a=$</span><input type="text" class="tinp" id="p7-iv2-a" value="3" style="width:80px">'
|
||||
+ '<button class="btn primary" id="p7-iv2-calc">Вычислить</button>'
|
||||
+ '</div>'
|
||||
+ '<div id="p7-iv2-out" style="font-size:.95rem;line-height:1.7"></div>'
|
||||
+ '</div>';
|
||||
|
||||
/* === ИНТЕРАКТИВ 3 — Двойственные пары (DnD) === */
|
||||
html += '<div class="wg" id="p7-iv3">'
|
||||
+ '<div class="wg-header"><span class="wg-badge">DnD \xb7 двойственность</span><div class="wg-title">Двойственные пары платоновых тел</div></div>'
|
||||
+ '<div class="wg-help">Перетащи $6$ карточек по $3$ ящикам так, чтобы в каждом ящике стояла правильная двойственная пара. После проверки — +15 XP.</div>'
|
||||
+ '<div id="p7-iv3-pool" class="dnd-pool"></div>'
|
||||
+ '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-top:10px">'
|
||||
+ '<div class="drop-box"><h5>Пара 1</h5><div class="drop-items" data-cat="pair1"></div></div>'
|
||||
+ '<div class="drop-box"><h5>Пара 2</h5><div class="drop-items" data-cat="pair2"></div></div>'
|
||||
+ '<div class="drop-box"><h5>Пара 3</h5><div class="drop-items" data-cat="pair3"></div></div>'
|
||||
+ '</div>'
|
||||
+ '<div class="actions" style="margin-top:10px;display:flex;gap:6px"><button class="btn primary" id="p7-iv3-check">Проверить</button><button class="btn" id="p7-iv3-reset">Сброс</button></div>'
|
||||
+ '<div class="feedback" id="p7-iv3-fb"></div>'
|
||||
+ '</div>';
|
||||
|
||||
/* === ИНТЕРАКТИВ 4 — Тренажёр === */
|
||||
html += '<div class="wg" id="p7-iv4">'
|
||||
+ '<div class="wg-header"><span class="wg-badge">тренажёр \xb7 6 задач</span><div class="wg-title">Грани, вершины, рёбра и объёмы</div></div>'
|
||||
+ '<div class="wg-help">Введи числовой ответ. Допуск $\\pm 0{,}05$ для дробных значений.</div>'
|
||||
+ '<div id="p7-iv4-list"></div>'
|
||||
+ '<div class="score-display" style="margin-top:10px">Решено: <b id="p7-iv4-score">0</b> / 6</div>'
|
||||
+ '</div>';
|
||||
|
||||
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 = '<p style="font-weight:700;font-size:1rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:6px">' + inf.name + '</p>'
|
||||
+ '<div style="display:flex;gap:14px;flex-wrap:wrap;font-size:.9rem;margin-bottom:6px">'
|
||||
+ '<span>Граней $F=$<b>' + inf.F + '</b></span>'
|
||||
+ '<span>Вершин $V=$<b>' + inf.V + '</b></span>'
|
||||
+ '<span>Рёбер $E=$<b>' + inf.E + '</b></span>'
|
||||
+ '<span>В вершине рёбер: <b>' + inf.vAtVertex + '</b></span>'
|
||||
+ '</div>'
|
||||
+ '<p>$' + inf.volFmt + '\\approx ' + fmt(+Vnum.toFixed(3)) + '$ (для $a=' + fmt(a) + '$)</p>'
|
||||
+ '<p>$' + inf.areaFmt + '\\approx ' + fmt(+Snum.toFixed(3)) + '$</p>'
|
||||
+ '<p style="font-size:.84rem;color:var(--muted);margin-top:4px">Проверка Эйлера: $V-E+F=' + inf.V + '-' + inf.E + '+' + inf.F + '=' + (inf.V - inf.E + inf.F) + '$.</p>';
|
||||
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 = '<span style="color:var(--bad)">✗ Введи $a>0$.</span>';
|
||||
renderMath(outBox);
|
||||
return;
|
||||
}
|
||||
const inf = PLATONIC_INFO[kind];
|
||||
const V = inf.volume(a);
|
||||
const S = inf.area(a);
|
||||
outBox.innerHTML = '<p><b>' + inf.name + '</b> с длиной ребра $a=' + fmt(a) + '$:</p>'
|
||||
+ '<p>$' + inf.volFmt + ' \\approx ' + fmt(+V.toFixed(4)) + '$</p>'
|
||||
+ '<p>$' + inf.areaFmt + ' \\approx ' + fmt(+S.toFixed(4)) + '$</p>'
|
||||
+ '<p style="font-size:.84rem;color:var(--muted)">Граней: $F=' + inf.F + '$, вершин: $V=' + inf.V + '$, рёбер: $E=' + inf.E + '$.</p>';
|
||||
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, '✓ Верно! Двойственные пары: <b>тетраэдр $\\leftrightarrow$ тетраэдр</b>, <b>куб $\\leftrightarrow$ октаэдр</b>, <b>додекаэдр $\\leftrightarrow$ икосаэдр</b>.');
|
||||
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 '<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:10px 12px;margin-bottom:8px">'
|
||||
+ '<div style="margin-bottom:6px"><b>Задача ' + (i+1) + '.</b> ' + t.q + '</div>'
|
||||
+ '<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center">'
|
||||
+ '<input type="text" class="tinp" id="p7-iv4-inp-' + i + '" placeholder="число" style="width:140px">'
|
||||
+ '<button class="btn primary" data-i="' + i + '">Проверить</button>'
|
||||
+ '</div>'
|
||||
+ '<div class="feedback" id="p7-iv4-fb-' + i + '"></div>'
|
||||
+ '</div>';
|
||||
}).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(){
|
||||
|
||||
Reference in New Issue
Block a user