fix(geom8 ch2): §12 равносторонний крупнее + §14 тип треугольника крупнее + §11 доказательство Пифагора корректное + §15 таблица троек красивее

§12 Равносторонний — слайдер a: viewBox 300×260 → 420×320, scale 10 → 14;
треугольник теперь занимает большую часть SVG. Добавлены точки и крупные
буквы вершин (Unbounded), цветовые подписи h и S в SVG.

§14 Тяни стороны — тип треугольника: viewBox 320×240 → 440×320, переписана
функция drawTriSVG: всегда использует наибольшую сторону как основание
(стабильная компоновка), масштаб подгоняется под доступную площадь.
Крупные подписи вершин с точками, форматированные подписи сторон.

§11 Доказательство квадрат (a+b)²: 2-й, 3-й и 4-й треугольники имели
оба катета одинаковой длины (=a вместо a и b). Полностью переписана
геометрия:
  - T1 (top-left): legs a (вертикаль) и b (горизонталь)
  - T2 (top-right): legs a (горизонталь) и b (вертикаль)
  - T3, T4: повторение поворотом 90°
  - Внутренний квадрат (off,off+a)-(off+b,off)-(off+S,off+b)-(off+a,off+S)
    с реально равными сторонами c=√(a²+b²)
Каждый треугольник — своего цвета. Шаги переработаны: 1) большой квадрат
с (a+b)², 2) 4 треугольника, 3) внутренний квадрат c², 4) уравнение
площадей, 5) вывод c²=a²+b². ViewBox 360×240 → 440×380.

§15 10 троек Пифагора: каждая тройка теперь в виде карточки с фоном
(зелёный для примитивных, оранжевый для кратных), бейджем 'ПРИМ' / '×k',
мини-SVG треугольником, формулой a²+b²=c² и hover-анимацией. Подобранные
тройки: 6 примитивных (3-4-5, 5-12-13, 7-24-25, 8-15-17, 9-40-41,
20-21-29, 11-60-61, 12-35-37) + 2 кратные (6,8,10) и (10,24,26).
Большая детальная SVG с подписями + 'a²+b²=c²' в численном виде.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-28 14:41:24 +03:00
parent 4803f970c1
commit e0e3280404
+176 -109
View File
@@ -4943,46 +4943,69 @@ function buildP11(){
/* == INIT: Анимация доказательства == */
(function(){
let step=0;
const W=360, H=240;
const S=160, off=20; // big square side, offset
// a=100, b=60 in px (a+b=160)
const a=100, b=60;
// 4 triangle vertices in big square at off,off:
// TL: (off,off+a)-(off,off)-(off+b,off)
// TR: (off+b,off)-(off+S,off)-(off+S,off+b)... wait use standard rotation:
// Let inner square vertices at: (off+b,off),(off+S,off+a),(off+a,off+S),(off,off+b)
const iv=[[off+b,off],[off+S,off+a],[off+a,off+S],[off,off+b]];
function ivStr(){return iv.map(p=>p[0]+','+p[1]).join(' ');}
const triStroke='#0d9488';
const triFill='rgba(13,148,136,.28)';
function drawTris(){
return '<polygon points="'+off+','+(off+a)+' '+off+','+off+' '+(off+b)+','+off+'" fill="'+triFill+'" stroke="'+triStroke+'" stroke-width="1.2"/>'
+'<polygon points="'+(off+b)+','+off+' '+(off+S)+','+off+' '+(off+S)+','+(off+a)+'" fill="'+triFill+'" stroke="'+triStroke+'" stroke-width="1.2"/>'
+'<polygon points="'+(off+S)+','+(off+a)+' '+(off+S)+','+(off+S)+' '+(off+a)+','+(off+S)+'" fill="'+triFill+'" stroke="'+triStroke+'" stroke-width="1.2"/>'
+'<polygon points="'+off+','+(off+b)+' '+(off+a)+','+(off+S)+' '+off+','+(off+S)+'" fill="'+triFill+'" stroke="'+triStroke+'" stroke-width="1.2"/>';
const W=440, H=380;
/* Big square of side (a+b). a=120, b=80 → S=200. Margin 30. */
const a=120, b=80, S=a+b, off=40;
/* 4 right triangles in corners, each with legs a and b, rotated 90° around CCW. */
const T1=[[off,off+a],[off,off],[off+b,off]];
const T2=[[off+b,off],[off+S,off],[off+S,off+b]];
const T3=[[off+S,off+b],[off+S,off+S],[off+a,off+S]];
const T4=[[off+a,off+S],[off,off+S],[off,off+a]];
/* Inner square vertices = hypotenuse endpoints. */
const IV=[[off,off+a],[off+b,off],[off+S,off+b],[off+a,off+S]];
function poly(pts,fill,stroke,sw){
return '<polygon points="'+pts.map(p=>p[0]+','+p[1]).join(' ')+'" fill="'+fill+'" stroke="'+stroke+'" stroke-width="'+sw+'"/>';
}
const triColors=['#0d9488','#7c3aed','#dc2626','#f59e0b'];
const triFills=['rgba(13,148,136,.28)','rgba(124,58,237,.28)','rgba(220,38,38,.25)','rgba(245,158,11,.28)'];
function drawTris(highlightIdx){
return [T1,T2,T3,T4].map((T,i)=>{
const hl=highlightIdx===undefined||highlightIdx===i;
return poly(T, hl?triFills[i]:'rgba(0,0,0,.04)', hl?triColors[i]:'#94a3b8', hl?1.5:1);
}).join('');
}
function labelLegs(){
/* a, b labels on the 4 edges of big square (each side = a+b, split at b and a) */
let s='';
s+='<text x="'+(off+b/2)+'" y="'+(off-8)+'" text-anchor="middle" font-size="13" font-weight="700" fill="#0f766e" font-family="JetBrains Mono,monospace">b</text>';
s+='<text x="'+(off+b+a/2)+'" y="'+(off-8)+'" text-anchor="middle" font-size="13" font-weight="700" fill="#0f766e" font-family="JetBrains Mono,monospace">a</text>';
s+='<text x="'+(off-12)+'" y="'+(off+a/2+5)+'" text-anchor="middle" font-size="13" font-weight="700" fill="#0f766e" font-family="JetBrains Mono,monospace">a</text>';
s+='<text x="'+(off-12)+'" y="'+(off+a+b/2+5)+'" text-anchor="middle" font-size="13" font-weight="700" fill="#0f766e" font-family="JetBrains Mono,monospace">b</text>';
return s;
}
const steps=[
{desc:'Большой квадрат со стороной $(a+b)$. Площадь $(a+b)^2$.',
draw(){return '<rect x="'+off+'" y="'+off+'" width="'+S+'" height="'+S+'" fill="rgba(22,163,74,.10)" stroke="#16a34a" stroke-width="2.5"/>'
+'<text x="'+(off+S/2)+'" y="'+(off+S+14)+'" text-anchor="middle" font-size="11" font-weight="700" fill="#15803d">(a+b)</text>';}},
{desc:'Внутри расположим 4 прямоугольных треугольника с катетами $a$ и $b$ (голубые).',
draw(){return '<rect x="'+off+'" y="'+off+'" width="'+S+'" height="'+S+'" fill="none" stroke="#16a34a" stroke-width="2"/>'+drawTris();}},
{desc:'Внутри треугольников остаётся квадрат со стороной $c$ (гипотенуза). Его площадь $= c^2$.',
draw(){return '<rect x="'+off+'" y="'+off+'" width="'+S+'" height="'+S+'" fill="none" stroke="#16a34a" stroke-width="2"/>'+drawTris()
+'<polygon points="'+ivStr()+'" fill="rgba(22,163,74,.35)" stroke="#16a34a" stroke-width="2.2"/>'
+'<text x="'+(off+S/2)+'" y="'+(off+S/2)+'" text-anchor="middle" dominant-baseline="middle" font-size="14" font-weight="800" fill="#15803d"></text>';}},
{desc:'Площадь 4 треугольников $= 4\\cdot\\dfrac{ab}{2}=2ab$.',
draw(){return '<rect x="'+off+'" y="'+off+'" width="'+S+'" height="'+S+'" fill="rgba(22,163,74,.08)" stroke="#16a34a" stroke-width="2"/>'+drawTris()
+'<text x="'+(off+18)+'" y="'+(off+16)+'" font-size="10" fill="#0f766e">4·(ab/2)=2ab</text>';}},
{desc:'$c^2 = (a+b)^2 - 2ab = a^2+2ab+b^2-2ab = a^2+b^2$. Теорема доказана! $\\square$',
draw(){return '<rect x="'+off+'" y="'+off+'" width="'+S+'" height="'+S+'" fill="rgba(22,163,74,.06)" stroke="#16a34a" stroke-width="2"/>'+drawTris()
+'<polygon points="'+ivStr()+'" fill="rgba(22,163,74,.35)" stroke="#16a34a" stroke-width="2.2"/>'
+'<text x="'+(off+S/2)+'" y="'+(off+S/2-8)+'" text-anchor="middle" font-size="12" font-weight="800" fill="#15803d">c²=a²+b²</text>'
+'<text x="'+(off+S/2)+'" y="'+(off+S/2+10)+'" text-anchor="middle" font-size="10" fill="#15803d">Q.E.D.</text>';}},
{desc:'Большой квадрат со стороной $(a+b)$. Его площадь равна $(a+b)^2$.',
draw(){return '<rect x="'+off+'" y="'+off+'" width="'+S+'" height="'+S+'" fill="rgba(22,163,74,.08)" stroke="#16a34a" stroke-width="2.5"/>'
+'<text x="'+(off+S/2)+'" y="'+(off+S/2+5)+'" text-anchor="middle" font-size="22" font-weight="800" fill="#15803d" font-family="Unbounded,sans-serif">(a+b)²</text>'
+'<text x="'+(off+S/2)+'" y="'+(off+S+24)+'" text-anchor="middle" font-size="14" font-weight="700" fill="#15803d" font-family="JetBrains Mono,monospace">a + b</text>'
+'<text x="'+(off-22)+'" y="'+(off+S/2+5)+'" text-anchor="middle" font-size="14" font-weight="700" fill="#15803d" font-family="JetBrains Mono,monospace" transform="rotate(-90,'+(off-22)+','+(off+S/2+5)+')">a + b</text>';}},
{desc:'Разместим в нём 4 одинаковых прямоугольных треугольника с катетами $a$ и $b$ (по одному в каждом углу).',
draw(){return '<rect x="'+off+'" y="'+off+'" width="'+S+'" height="'+S+'" fill="none" stroke="#16a34a" stroke-width="2"/>'
+drawTris()+labelLegs();}},
{desc:'Между треугольниками остался <b>квадрат</b>! Все его стороны равны гипотенузе $c$, все углы по $90°$. Его площадь $= c^2$.',
draw(){return '<rect x="'+off+'" y="'+off+'" width="'+S+'" height="'+S+'" fill="none" stroke="#16a34a" stroke-width="2"/>'
+drawTris()
+poly(IV,'rgba(22,163,74,.45)','#15803d',2.5)
+'<text x="'+(off+S/2)+'" y="'+(off+S/2+6)+'" text-anchor="middle" font-size="22" font-weight="800" fill="#15803d" font-family="Unbounded,sans-serif"></text>'
+labelLegs();}},
{desc:'Площадь большого квадрата = площадь 4 треугольников + площадь внутреннего квадрата:<br>$(a+b)^2 = 4\\cdot\\dfrac{ab}{2} + c^2 = 2ab + c^2$.',
draw(){return '<rect x="'+off+'" y="'+off+'" width="'+S+'" height="'+S+'" fill="none" stroke="#16a34a" stroke-width="2"/>'
+drawTris()
+poly(IV,'rgba(22,163,74,.32)','#15803d',2)
+'<text x="'+(off+S/2)+'" y="'+(off+S/2-4)+'" text-anchor="middle" font-size="13" font-weight="800" fill="#15803d"></text>'
+'<text x="'+(off+S/2)+'" y="'+(off+S/2+14)+'" text-anchor="middle" font-size="11" fill="#0f766e">+ 4·(ab/2)</text>'
+labelLegs();}},
{desc:'Раскроем $(a+b)^2 = a^2 + 2ab + b^2$. Подставим: $a^2+2ab+b^2 = 2ab + c^2$. Отсюда <b>$c^2 = a^2 + b^2$</b>. $\\blacksquare$',
draw(){return '<rect x="'+off+'" y="'+off+'" width="'+S+'" height="'+S+'" fill="rgba(22,163,74,.06)" stroke="#16a34a" stroke-width="2"/>'
+drawTris()
+poly(IV,'rgba(22,163,74,.42)','#15803d',2.5)
+'<text x="'+(off+S/2)+'" y="'+(off+S/2-8)+'" text-anchor="middle" font-size="18" font-weight="800" fill="#15803d" font-family="Unbounded,sans-serif">c² = a² + b²</text>'
+'<text x="'+(off+S/2)+'" y="'+(off+S/2+18)+'" text-anchor="middle" font-size="12" fill="#15803d">теорема доказана</text>'
+labelLegs();}},
];
function render(){
const s=steps[step];
document.getElementById('p11-proof-svg-wrap').innerHTML='<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:380px;background:var(--card);border:1px solid var(--border);border-radius:14px">'+s.draw()+'</svg>';
document.getElementById('p11-proof-svg-wrap').innerHTML='<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:460px;background:var(--card);border:1px solid var(--border);border-radius:14px">'+s.draw()+'</svg>';
document.getElementById('p11-proof-desc').innerHTML='<b>Шаг '+(step+1)+' / '+steps.length+'.</b> '+s.desc;
document.getElementById('p11-proof-next').textContent=step<steps.length-1?'Далее':'Готово!';
if(window.renderMathInElement)renderMath(document.getElementById('p11-proof-desc'));
@@ -5283,36 +5306,38 @@ function buildP12(){
/* == INIT: Слайдер == */
(function(){
const sl=document.getElementById('p12-sl-a');
const W=300, H=260;
const W=420, H=320;
function draw(){
const a=+sl.value;
document.getElementById('p12-sl-a-val').textContent=a;
const scale=Math.min(10, 200/a);
const scale=14;
const px=a*scale;
const h=a*Math.sqrt(3)/2;
const hVal=a*Math.sqrt(3)/2;
const S=a*a*Math.sqrt(3)/4;
const cx=W/2, baseY=H-30, topY=baseY-h*scale;
const cx=W/2, baseY=H-44, topY=baseY-hVal*scale;
const bx1=cx-px/2, bx2=cx+px/2;
const midX=cx;
let s='<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:320px;background:var(--card);border:1px solid var(--border);border-radius:14px">';
s+='<polygon points="'+cx+','+topY+' '+bx1+','+baseY+' '+bx2+','+baseY+'" fill="rgba(34,197,94,.2)" stroke="#16a34a" stroke-width="2.2"/>';
s+='<line x1="'+cx+'" y1="'+topY+'" x2="'+midX+'" y2="'+baseY+'" stroke="#15803d" stroke-width="2" stroke-dasharray="5 3"/>';
const rm=8;
s+='<polyline points="'+(midX-rm)+','+baseY+' '+(midX-rm)+','+(baseY-rm)+' '+midX+','+(baseY-rm)+'" fill="none" stroke="#15803d" stroke-width="1.5"/>';
s+='<text x="'+(cx-5)+'" y="'+(topY-8)+'" font-size="11" font-weight="700" fill="#15803d">A</text>';
s+='<text x="'+(bx1-14)+'" y="'+(baseY+14)+'" font-size="11" font-weight="700" fill="#15803d">B</text>';
s+='<text x="'+(bx2+4)+'" y="'+(baseY+14)+'" font-size="11" font-weight="700" fill="#15803d">C</text>';
s+='<text x="'+(cx+5)+'" y="'+Math.round((topY+baseY)/2)+'" font-size="10" font-weight="700" fill="#166534">h</text>';
s+='<text x="'+Math.round(cx-px/4-10)+'" y="'+Math.round((topY+baseY)/2+8)+'" font-size="9" fill="#15803d">a</text>';
s+='<text x="'+Math.round(cx+px/4)+'" y="'+Math.round((topY+baseY)/2+8)+'" font-size="9" fill="#15803d">a</text>';
s+='<text x="'+Math.round(cx-px/4)+'" y="'+(baseY+15)+'" text-anchor="middle" font-size="9" fill="#15803d">a</text>';
let s='<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:440px;background:var(--card);border:1px solid var(--border);border-radius:14px">';
s+='<polygon points="'+cx+','+topY+' '+bx1+','+baseY+' '+bx2+','+baseY+'" fill="rgba(34,197,94,.18)" stroke="#16a34a" stroke-width="2.5" stroke-linejoin="round"/>';
s+='<line x1="'+cx+'" y1="'+topY+'" x2="'+cx+'" y2="'+baseY+'" stroke="#dc2626" stroke-width="2" stroke-dasharray="6 3"/>';
s+='<polyline points="'+(cx-10)+','+baseY+' '+(cx-10)+','+(baseY-10)+' '+cx+','+(baseY-10)+'" fill="none" stroke="#dc2626" stroke-width="1.5"/>';
s+='<circle cx="'+cx+'" cy="'+topY+'" r="3.5" fill="#15803d"/>';
s+='<circle cx="'+bx1+'" cy="'+baseY+'" r="3.5" fill="#15803d"/>';
s+='<circle cx="'+bx2+'" cy="'+baseY+'" r="3.5" fill="#15803d"/>';
s+='<circle cx="'+cx+'" cy="'+baseY+'" r="2.5" fill="#dc2626"/>';
s+='<text x="'+cx+'" y="'+(topY-12)+'" text-anchor="middle" font-size="15" font-weight="800" fill="#15803d" font-family="Unbounded,sans-serif">A</text>';
s+='<text x="'+(bx1-16)+'" y="'+(baseY+18)+'" font-size="15" font-weight="800" fill="#15803d" font-family="Unbounded,sans-serif">B</text>';
s+='<text x="'+(bx2+6)+'" y="'+(baseY+18)+'" font-size="15" font-weight="800" fill="#15803d" font-family="Unbounded,sans-serif">C</text>';
s+='<text x="'+(cx+18)+'" y="'+((topY+baseY)/2+5)+'" font-size="14" font-weight="800" fill="#b91c1c" font-family="JetBrains Mono,monospace">h='+hVal.toFixed(2)+'</text>';
s+='<text x="'+((bx1+cx)/2-6)+'" y="'+((topY+baseY)/2+12)+'" text-anchor="end" font-size="13" font-weight="700" fill="#15803d" font-family="JetBrains Mono,monospace">a='+a+'</text>';
s+='<text x="'+((cx+bx2)/2+6)+'" y="'+((topY+baseY)/2+12)+'" font-size="13" font-weight="700" fill="#15803d" font-family="JetBrains Mono,monospace">a='+a+'</text>';
s+='<text x="'+cx+'" y="'+(baseY+34)+'" text-anchor="middle" font-size="13" font-weight="700" fill="#15803d" font-family="JetBrains Mono,monospace">a = '+a+' см</text>';
s+='<text x="20" y="26" font-size="14" font-weight="800" fill="#0e7490" font-family="Unbounded,sans-serif">S = '+S.toFixed(2)+' см²</text>';
s+='</svg>';
document.getElementById('p12-sl-svg-wrap').innerHTML=s;
const hVal=(a*Math.sqrt(3)/2);
document.getElementById('p12-sl-info').innerHTML=`
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">Сторона a</div><b>${a} см</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">Высота h</div><b style="color:#15803d">${hVal.toFixed(3)} см</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">Площадь S</div><b style="color:#0e7490">${S.toFixed(3)} см²</b></div>`;
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">Высота h = a√3/2</div><b style="color:#b91c1c">${hVal.toFixed(3)} см</b></div>
<div style="padding:8px 12px;background:var(--card);border-radius:8px;border:1px solid var(--border);font-size:.88rem"><div style="color:var(--muted);font-size:.72rem;font-weight:700;text-transform:uppercase;margin-bottom:4px">Площадь S = a²√3/4</div><b style="color:#0e7490">${S.toFixed(3)} см²</b></div>`;
}
sl.addEventListener('input',()=>{draw();addXp(1,'p12-sl');});
draw();
@@ -5930,7 +5955,7 @@ function buildP14(){
const slA=document.getElementById('p14-sl-a');
const slB=document.getElementById('p14-sl-b');
const slC=document.getElementById('p14-sl-c');
const W=320, H=240;
const W=440, H=320;
function triType(a,b,c){
const sides=[a,b,c].sort((x,y)=>x-y);
const [s1,s2,s3]=sides;
@@ -5941,24 +5966,36 @@ function buildP14(){
return 'obtuse';
}
function drawTriSVG(a,b,c){
// Place right angle at origin for display, use cosine rule for coords
const sides=[a,b,c].sort((x,y)=>x-y);
// angle A opposite side a (smallest):
const cosC=(a*a+b*b-c*c)/(2*a*b);
const sinC=Math.sqrt(Math.max(0,1-cosC*cosC));
const scale=Math.min(10,140/c);
const bx=a*scale;
const cx2=b*scale*cosC, cy2=b*scale*sinC;
const ox=(W-bx)/2, oy=H-40;
const P1={x:ox,y:oy}, P2={x:ox+bx,y:oy}, P3={x:ox+cx2,y:oy-cy2};
let s='<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:340px;background:var(--card);border:1px solid var(--border);border-radius:14px">';
s+='<polygon points="'+P1.x+','+P1.y+' '+P2.x+','+P2.y+' '+P3.x+','+P3.y+'" fill="rgba(217,119,6,.18)" stroke="#b45309" stroke-width="2.2"/>';
s+='<text x="'+(P1.x-14)+'" y="'+(P1.y+5)+'" font-size="11" font-weight="700" fill="#b45309">A</text>';
s+='<text x="'+(P2.x+4)+'" y="'+(P2.y+5)+'" font-size="11" font-weight="700" fill="#b45309">B</text>';
s+='<text x="'+(P3.x-4)+'" y="'+(P3.y-6)+'" font-size="11" font-weight="700" fill="#b45309">C</text>';
s+='<text x="'+Math.round((P1.x+P2.x)/2)+'" y="'+(P1.y+14)+'" text-anchor="middle" font-size="9" fill="#92400e">c='+c+'</text>';
s+='<text x="'+Math.round((P1.x+P3.x)/2-12)+'" y="'+Math.round((P1.y+P3.y)/2)+'" font-size="9" fill="#92400e">b='+b+'</text>';
s+='<text x="'+Math.round((P2.x+P3.x)/2+4)+'" y="'+Math.round((P2.y+P3.y)/2)+'" font-size="9" fill="#92400e">a='+a+'</text>';
/* Use largest side as base for stable layout. Place base horizontally bottom. */
const sides=[{v:a,name:'a'},{v:b,name:'b'},{v:c,name:'c'}].sort((x,y)=>x.v-y.v);
const s1=sides[0].v, s2=sides[1].v, s3=sides[2].v;
/* Compute apex via cosine rule. Base=s3 (largest) along bottom. */
const cosA=(s2*s2+s3*s3-s1*s1)/(2*s2*s3);
const sinA=Math.sqrt(Math.max(0,1-cosA*cosA));
const apexH=s2*sinA;
/* Available area: 380×240, with margins. Scale to fit. */
const scale=Math.min(360/s3, 230/Math.max(apexH,1));
const bx=s3*scale, by=apexH*scale;
const ox=(W-bx)/2, oy=H-46;
const P1={x:ox,y:oy};
const P2={x:ox+bx,y:oy};
const apexX=s2*scale*cosA;
const P3={x:ox+apexX,y:oy-by};
/* Determine which side is which by matching sorted order back to a/b/c.
s3 = max, on base. s1 = min, s2 = mid. Labels follow sorted assignment. */
const lblBase=sides[2].name, lblLeft=sides[1].name, lblRight=sides[0].name;
const vBase=s3, vLeft=s2, vRight=s1;
let s='<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;max-width:460px;background:var(--card);border:1px solid var(--border);border-radius:14px">';
s+='<polygon points="'+P1.x+','+P1.y+' '+P2.x+','+P2.y+' '+P3.x+','+P3.y+'" fill="rgba(217,119,6,.18)" stroke="#b45309" stroke-width="2.5" stroke-linejoin="round"/>';
s+='<circle cx="'+P1.x+'" cy="'+P1.y+'" r="4" fill="#b45309"/>';
s+='<circle cx="'+P2.x+'" cy="'+P2.y+'" r="4" fill="#b45309"/>';
s+='<circle cx="'+P3.x+'" cy="'+P3.y+'" r="4" fill="#b45309"/>';
s+='<text x="'+(P1.x-16)+'" y="'+(P1.y+20)+'" font-size="16" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">A</text>';
s+='<text x="'+(P2.x+6)+'" y="'+(P2.y+20)+'" font-size="16" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">B</text>';
s+='<text x="'+(P3.x-6)+'" y="'+(P3.y-12)+'" font-size="16" font-weight="800" fill="#b45309" font-family="Unbounded,sans-serif">C</text>';
s+='<text x="'+((P1.x+P2.x)/2)+'" y="'+(P1.y+24)+'" text-anchor="middle" font-size="13" font-weight="700" fill="#92400e" font-family="JetBrains Mono,monospace">'+lblBase+' = '+vBase+'</text>';
s+='<text x="'+((P1.x+P3.x)/2-20)+'" y="'+((P1.y+P3.y)/2+5)+'" text-anchor="end" font-size="13" font-weight="700" fill="#92400e" font-family="JetBrains Mono,monospace">'+lblLeft+' = '+vLeft+'</text>';
s+='<text x="'+((P2.x+P3.x)/2+10)+'" y="'+((P2.y+P3.y)/2+5)+'" font-size="13" font-weight="700" fill="#92400e" font-family="JetBrains Mono,monospace">'+lblRight+' = '+vRight+'</text>';
s+='</svg>';
return s;
}
@@ -6331,60 +6368,90 @@ function buildP15(){
/* == INIT: Таблица троек == */
(function(){
const triples=[
[3,4,5],[5,12,13],[7,24,25],[8,15,17],[9,40,41],
[6,8,10],[10,24,26],[12,16,20],[15,20,25],[20,21,29],
{a:3,b:4,c:5,kind:'prim'},
{a:5,b:12,c:13,kind:'prim'},
{a:7,b:24,c:25,kind:'prim'},
{a:8,b:15,c:17,kind:'prim'},
{a:9,b:40,c:41,kind:'prim'},
{a:20,b:21,c:29,kind:'prim'},
{a:11,b:60,c:61,kind:'prim'},
{a:12,b:35,c:37,kind:'prim'},
{a:6,b:8,c:10,kind:'mul',base:'(3,4,5)',k:2},
{a:10,b:24,c:26,kind:'mul',base:'(5,12,13)',k:2},
];
const wrap=document.getElementById('p15-table-wrap');
const svgBox=document.getElementById('p15-table-svg');
let sel=-1;
function drawMini(a,b,c,active){
const sc=Math.min(5.5,40/Math.max(a,b,c));
const ax=a*sc, by=b*sc;
const ox=10, oy=50+by;
let s='<svg viewBox="0 0 '+(ax+20)+' '+(by+20)+'" style="width:100%;max-height:50px">';
s+='<polygon points="'+ox+','+oy+' '+(ox+ax)+','+oy+' '+ox+','+(oy-by)+'" fill="rgba(5,150,105,.25)" stroke="#059669" stroke-width="1.5"/>';
s+='<polyline points="'+(ox+6)+','+oy+' '+(ox+6)+','+(oy-6)+' '+ox+','+(oy-6)+'" fill="none" stroke="#059669" stroke-width="1.2"/>';
/* Override grid for nicer cards */
wrap.style.gridTemplateColumns='repeat(auto-fill,minmax(170px,1fr))';
wrap.style.gap='12px';
function drawMini(a,b,c){
const sc=Math.min(2.4, 55/Math.max(a,b));
const ax=Math.max(28,a*sc), by=Math.max(28,b*sc);
const W=ax+24, H=by+24;
const ox=12, oy=H-10;
let s='<svg viewBox="0 0 '+W+' '+H+'" style="width:100%;max-height:90px">';
s+='<polygon points="'+ox+','+oy+' '+(ox+ax)+','+oy+' '+ox+','+(oy-by)+'" fill="rgba(5,150,105,.22)" stroke="#059669" stroke-width="1.8" stroke-linejoin="round"/>';
s+='<polyline points="'+(ox+8)+','+oy+' '+(ox+8)+','+(oy-8)+' '+ox+','+(oy-8)+'" fill="none" stroke="#059669" stroke-width="1.3"/>';
s+='</svg>';
return s;
}
function showSVG(i){
const [a,b,c]=triples[i];
const sc=Math.min(8,160/Math.max(a,b,c));
const ax=a*sc, by=b*sc;
const W=Math.round(ax+80), H=Math.round(by+60);
const ox=40, oy=H-30;
let s='<svg viewBox="0 0 '+W+' '+H+'" style="max-width:360px;width:100%;background:var(--card);border:1px solid var(--border);border-radius:14px">';
s+='<polygon points="'+ox+','+oy+' '+(ox+ax)+','+oy+' '+ox+','+(oy-by)+'" fill="rgba(5,150,105,.2)" stroke="#059669" stroke-width="2.2"/>';
const rm=9;
const t=triples[i];
const sc=Math.min(9, 200/Math.max(t.a,t.b));
const ax=t.a*sc, by=t.b*sc;
const W=Math.round(ax+140), H=Math.round(by+90);
const ox=50, oy=H-44;
let s='<svg viewBox="0 0 '+W+' '+H+'" style="max-width:460px;width:100%;background:var(--card);border:1px solid var(--border);border-radius:14px">';
s+='<polygon points="'+ox+','+oy+' '+(ox+ax)+','+oy+' '+ox+','+(oy-by)+'" fill="rgba(5,150,105,.20)" stroke="#059669" stroke-width="2.5" stroke-linejoin="round"/>';
const rm=11;
s+='<polyline points="'+(ox+rm)+','+oy+' '+(ox+rm)+','+(oy-rm)+' '+ox+','+(oy-rm)+'" fill="none" stroke="#059669" stroke-width="1.6"/>';
s+='<text x="'+(ox-14)+'" y="'+(oy+5)+'" font-size="11" font-weight="700" fill="#059669">B</text>';
s+='<text x="'+(ox+ax+4)+'" y="'+(oy+5)+'" font-size="11" font-weight="700" fill="#059669">C</text>';
s+='<text x="'+(ox-14)+'" y="'+(oy-by-2)+'" font-size="11" font-weight="700" fill="#059669">A</text>';
s+='<text x="'+Math.round(ox+ax/2)+'" y="'+(oy+16)+'" text-anchor="middle" font-size="10" font-weight="700" fill="#059669">a='+a+'</text>';
s+='<text x="'+(ox-22)+'" y="'+Math.round(oy-by/2)+'" font-size="10" font-weight="700" fill="#047857">b='+b+'</text>';
const hpx=(ox+ax+ox)/2+16, hpy=(oy+oy-by)/2;
s+='<text x="'+Math.round(hpx)+'" y="'+Math.round(hpy)+'" font-size="10" font-weight="700" fill="#0d9488">c='+c+'</text>';
s+='<text x="'+Math.round(W/2)+'" y="20" text-anchor="middle" font-size="10" fill="#047857">'+a+'²+'+b+'²='+c+'² &nbsp; ('+a*a+'+'+b*b+'='+c*c+')</text>';
s+='<circle cx="'+ox+'" cy="'+oy+'" r="3.5" fill="#059669"/>';
s+='<circle cx="'+(ox+ax)+'" cy="'+oy+'" r="3.5" fill="#059669"/>';
s+='<circle cx="'+ox+'" cy="'+(oy-by)+'" r="3.5" fill="#059669"/>';
s+='<text x="'+(ox-16)+'" y="'+(oy+18)+'" font-size="14" font-weight="800" fill="#059669" font-family="Unbounded,sans-serif">B</text>';
s+='<text x="'+(ox+ax+8)+'" y="'+(oy+18)+'" font-size="14" font-weight="800" fill="#059669" font-family="Unbounded,sans-serif">C</text>';
s+='<text x="'+(ox-16)+'" y="'+(oy-by-6)+'" font-size="14" font-weight="800" fill="#059669" font-family="Unbounded,sans-serif">A</text>';
s+='<text x="'+Math.round(ox+ax/2)+'" y="'+(oy+22)+'" text-anchor="middle" font-size="13" font-weight="800" fill="#047857" font-family="JetBrains Mono,monospace">a = '+t.a+'</text>';
s+='<text x="'+(ox-30)+'" y="'+Math.round(oy-by/2+5)+'" text-anchor="middle" font-size="13" font-weight="800" fill="#047857" font-family="JetBrains Mono,monospace">b = '+t.b+'</text>';
s+='<text x="'+Math.round(ox+ax/2+16)+'" y="'+Math.round(oy-by/2-5)+'" font-size="13" font-weight="800" fill="#0d9488" font-family="JetBrains Mono,monospace">c = '+t.c+'</text>';
const badge=t.kind==='prim'?'примитивная':'×'+t.k+' от '+t.base;
const bColor=t.kind==='prim'?'#15803d':'#b45309';
const bBg=t.kind==='prim'?'#d1fae5':'#fef3c7';
s+='<text x="20" y="24" font-size="11" font-weight="800" fill="'+bColor+'" font-family="Unbounded,sans-serif">'+badge.toUpperCase()+'</text>';
s+='<text x="'+(W-20)+'" y="24" text-anchor="end" font-size="13" font-weight="800" fill="#15803d" font-family="JetBrains Mono,monospace">'+t.a+'² + '+t.b+'² = '+t.c+'²</text>';
s+='<text x="'+(W-20)+'" y="42" text-anchor="end" font-size="11" fill="#047857" font-family="JetBrains Mono,monospace">'+(t.a*t.a)+' + '+(t.b*t.b)+' = '+(t.c*t.c)+'</text>';
s+='</svg>';
svgBox.innerHTML=s;
}
wrap.innerHTML=triples.map(([a,b,c],i)=>`
<div id="p15-tri-btn${i}" style="background:var(--card);border:1.5px solid var(--border);border-radius:10px;padding:8px;cursor:pointer;text-align:center;transition:border-color .15s">
<div style="font-weight:700;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));">(${a}, ${b}, ${c})</div>
${drawMini(a,b,c,false)}
</div>`).join('');
wrap.innerHTML=triples.map((t,i)=>{
const primBg=t.kind==='prim'?'linear-gradient(135deg,#ecfccb,#dcfce7)':'linear-gradient(135deg,#fef9c3,#fef3c7)';
const badgeBg=t.kind==='prim'?'#15803d':'#b45309';
const badgeText=t.kind==='prim'?'ПРИМ':'×'+t.k;
return `
<div id="p15-tri-btn${i}" style="background:${primBg};border:1.5px solid var(--border);border-radius:12px;padding:10px;cursor:pointer;text-align:center;transition:transform .15s,box-shadow .15s,border-color .15s;position:relative">
<div style="position:absolute;top:6px;right:6px;background:${badgeBg};color:#fff;font-size:.62rem;font-weight:800;padding:2px 6px;border-radius:4px;font-family:Unbounded,sans-serif">${badgeText}</div>
<div style="font-weight:800;font-size:1rem;color:#15803d;font-family:Unbounded,sans-serif;margin-bottom:2px">(${t.a}, ${t.b}, ${t.c})</div>
${drawMini(t.a,t.b,t.c)}
<div style="font-size:.7rem;color:#047857;font-family:JetBrains Mono,monospace;margin-top:2px">${t.a}²+${t.b}²=${t.c}²</div>
</div>`;
}).join('');
triples.forEach((_,i)=>{
document.getElementById('p15-tri-btn'+i).addEventListener('click',()=>{
sel=i;
const btn=document.getElementById('p15-tri-btn'+i);
btn.addEventListener('mouseenter',()=>{ btn.style.transform='translateY(-2px)'; btn.style.boxShadow='0 6px 16px rgba(5,150,105,.18)'; });
btn.addEventListener('mouseleave',()=>{ btn.style.transform=''; btn.style.boxShadow=''; });
btn.addEventListener('click',()=>{
triples.forEach((_,j)=>{
const el=document.getElementById('p15-tri-btn'+j);
el.style.borderColor=j===i?'var(--sec-acc,var(--pri))':'var(--border)';
el.style.borderColor=j===i?'#15803d':'var(--border)';
el.style.borderWidth=j===i?'2.5px':'1.5px';
});
showSVG(i); addXp(1,'p15-table-'+i);
});
});
showSVG(0);
document.getElementById('p15-tri-btn0').style.borderColor='var(--sec-acc,var(--pri))';
const first=document.getElementById('p15-tri-btn0');
first.style.borderColor='#15803d';
first.style.borderWidth='2.5px';
})();
/* == INIT: Тренажёр == */