From e0e3280404ddf9692e01ce4178a0d315763a8994 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Thu, 28 May 2026 14:41:24 +0300 Subject: [PATCH] =?UTF-8?q?fix(geom8=20ch2):=20=C2=A712=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BD=D0=BE=D1=81=D1=82=D0=BE=D1=80=D0=BE=D0=BD=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=BA=D1=80=D1=83=D0=BF=D0=BD=D0=B5=D0=B5=20+?= =?UTF-8?q?=20=C2=A714=20=D1=82=D0=B8=D0=BF=20=D1=82=D1=80=D0=B5=D1=83?= =?UTF-8?q?=D0=B3=D0=BE=D0=BB=D1=8C=D0=BD=D0=B8=D0=BA=D0=B0=20=D0=BA=D1=80?= =?UTF-8?q?=D1=83=D0=BF=D0=BD=D0=B5=D0=B5=20+=20=C2=A711=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=BA=D0=B0=D0=B7=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D1=81=D1=82?= =?UTF-8?q?=D0=B2=D0=BE=20=D0=9F=D0=B8=D1=84=D0=B0=D0=B3=D0=BE=D1=80=D0=B0?= =?UTF-8?q?=20=D0=BA=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82=D0=BD=D0=BE=D0=B5?= =?UTF-8?q?=20+=20=C2=A715=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=D0=B0=20?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=B5=D0=BA=20=D0=BA=D1=80=D0=B0=D1=81=D0=B8?= =?UTF-8?q?=D0=B2=D0=B5=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §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) --- frontend/textbooks/geometry_8_ch2.html | 285 +++++++++++++++---------- 1 file changed, 176 insertions(+), 109 deletions(-) diff --git a/frontend/textbooks/geometry_8_ch2.html b/frontend/textbooks/geometry_8_ch2.html index ee0cee2..73a0270 100644 --- a/frontend/textbooks/geometry_8_ch2.html +++ b/frontend/textbooks/geometry_8_ch2.html @@ -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 '' - +'' - +'' - +''; + 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 ''; + } + 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+='b'; + s+='a'; + s+='a'; + s+='b'; + return s; } const steps=[ - {desc:'Большой квадрат со стороной $(a+b)$. Площадь $(a+b)^2$.', - draw(){return '' - +'(a+b)';}}, - {desc:'Внутри расположим 4 прямоугольных треугольника с катетами $a$ и $b$ (голубые).', - draw(){return ''+drawTris();}}, - {desc:'Внутри треугольников остаётся квадрат со стороной $c$ (гипотенуза). Его площадь $= c^2$.', - draw(){return ''+drawTris() - +'' - +'';}}, - {desc:'Площадь 4 треугольников $= 4\\cdot\\dfrac{ab}{2}=2ab$.', - draw(){return ''+drawTris() - +'4·(ab/2)=2ab';}}, - {desc:'$c^2 = (a+b)^2 - 2ab = a^2+2ab+b^2-2ab = a^2+b^2$. Теорема доказана! $\\square$', - draw(){return ''+drawTris() - +'' - +'c²=a²+b²' - +'Q.E.D.';}}, + {desc:'Большой квадрат со стороной $(a+b)$. Его площадь равна $(a+b)^2$.', + draw(){return '' + +'(a+b)²' + +'a + b' + +'a + b';}}, + {desc:'Разместим в нём 4 одинаковых прямоугольных треугольника с катетами $a$ и $b$ (по одному в каждом углу).', + draw(){return '' + +drawTris()+labelLegs();}}, + {desc:'Между треугольниками остался квадрат! Все его стороны равны гипотенузе $c$, все углы по $90°$. Его площадь $= c^2$.', + draw(){return '' + +drawTris() + +poly(IV,'rgba(22,163,74,.45)','#15803d',2.5) + +'' + +labelLegs();}}, + {desc:'Площадь большого квадрата = площадь 4 треугольников + площадь внутреннего квадрата:
$(a+b)^2 = 4\\cdot\\dfrac{ab}{2} + c^2 = 2ab + c^2$.', + draw(){return '' + +drawTris() + +poly(IV,'rgba(22,163,74,.32)','#15803d',2) + +'' + +'+ 4·(ab/2)' + +labelLegs();}}, + {desc:'Раскроем $(a+b)^2 = a^2 + 2ab + b^2$. Подставим: $a^2+2ab+b^2 = 2ab + c^2$. Отсюда $c^2 = a^2 + b^2$. $\\blacksquare$', + draw(){return '' + +drawTris() + +poly(IV,'rgba(22,163,74,.42)','#15803d',2.5) + +'c² = a² + b²' + +'теорема доказана' + +labelLegs();}}, ]; function render(){ const s=steps[step]; - document.getElementById('p11-proof-svg-wrap').innerHTML=''+s.draw()+''; + document.getElementById('p11-proof-svg-wrap').innerHTML=''+s.draw()+''; document.getElementById('p11-proof-desc').innerHTML='Шаг '+(step+1)+' / '+steps.length+'. '+s.desc; document.getElementById('p11-proof-next').textContent=step'; - s+=''; - s+=''; - const rm=8; - s+=''; - s+='A'; - s+='B'; - s+='C'; - s+='h'; - s+='a'; - s+='a'; - s+='a'; + let s=''; + s+=''; + s+=''; + s+=''; + s+=''; + s+=''; + s+=''; + s+=''; + s+='A'; + s+='B'; + s+='C'; + s+='h='+hVal.toFixed(2)+''; + s+='a='+a+''; + s+='a='+a+''; + s+='a = '+a+' см'; + s+='S = '+S.toFixed(2)+' см²'; s+=''; document.getElementById('p12-sl-svg-wrap').innerHTML=s; - const hVal=(a*Math.sqrt(3)/2); document.getElementById('p12-sl-info').innerHTML=`
Сторона a
${a} см
-
Высота h
${hVal.toFixed(3)} см
-
Площадь S
${S.toFixed(3)} см²
`; +
Высота h = a√3/2
${hVal.toFixed(3)} см
+
Площадь S = a²√3/4
${S.toFixed(3)} см²
`; } 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=''; - s+=''; - s+='A'; - s+='B'; - s+='C'; - s+='c='+c+''; - s+='b='+b+''; - s+='a='+a+''; + /* 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=''; + s+=''; + s+=''; + s+=''; + s+=''; + s+='A'; + s+='B'; + s+='C'; + s+=''+lblBase+' = '+vBase+''; + s+=''+lblLeft+' = '+vLeft+''; + s+=''+lblRight+' = '+vRight+''; s+=''; 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=''; - s+=''; - s+=''; + /* 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=''; + s+=''; + s+=''; s+=''; 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=''; - s+=''; - 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=''; + s+=''; + const rm=11; s+=''; - s+='B'; - s+='C'; - s+='A'; - s+='a='+a+''; - s+='b='+b+''; - const hpx=(ox+ax+ox)/2+16, hpy=(oy+oy-by)/2; - s+='c='+c+''; - s+=''+a+'²+'+b+'²='+c+'²   ('+a*a+'+'+b*b+'='+c*c+')'; + s+=''; + s+=''; + s+=''; + s+='B'; + s+='C'; + s+='A'; + s+='a = '+t.a+''; + s+='b = '+t.b+''; + s+='c = '+t.c+''; + const badge=t.kind==='prim'?'примитивная':'×'+t.k+' от '+t.base; + const bColor=t.kind==='prim'?'#15803d':'#b45309'; + const bBg=t.kind==='prim'?'#d1fae5':'#fef3c7'; + s+=''+badge.toUpperCase()+''; + s+=''+t.a+'² + '+t.b+'² = '+t.c+'²'; + s+=''+(t.a*t.a)+' + '+(t.b*t.b)+' = '+(t.c*t.c)+''; s+=''; svgBox.innerHTML=s; } - wrap.innerHTML=triples.map(([a,b,c],i)=>` -
-
(${a}, ${b}, ${c})
- ${drawMini(a,b,c,false)} -
`).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 ` +
+
${badgeText}
+
(${t.a}, ${t.b}, ${t.c})
+ ${drawMini(t.a,t.b,t.c)} +
${t.a}²+${t.b}²=${t.c}²
+
`; + }).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: Тренажёр == */