From e2fc78d1f1369531dd494702c586c78182dff8b2 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Thu, 28 May 2026 10:13:52 +0300 Subject: [PATCH] =?UTF-8?q?fix(geom8=20ch2):=20=C2=A75=20=D0=B2=D1=8B?= =?UTF-8?q?=D1=81=D0=BE=D1=82=D0=B0/=D0=B4=D0=BE=D0=BA=D0=B0=D0=B7=D0=B0?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D1=8C=D1=81=D1=82=D0=B2=D0=BE=20+=20=C2=A78?= =?UTF-8?q?=20=D0=BF=D1=80=D1=8F=D0=BC=D1=8B=D0=B5=20=D1=83=D0=B3=D0=BB?= =?UTF-8?q?=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §5 Draggable трапеция: - Высота теперь рисуется как вертикальная пунктирная линия В СЕРЕДИНЕ трапеции от верхнего основания до нижнего (с прямым углом у основания), а не уходит вертикально вверх от вершины A вне фигуры - Жёлтый drag-handle для h перенесён в вершину D (верх-лево) — тащишь её вертикально и высота меняется. Синий drag-handle для b остался в C. - Добавлены подписи всех вершин ABCD точками и Unbounded-буквами - Подсказки в углу SVG что какой цвет означает §5 Пошаговое доказательство: - Полностью переписана геометрия с КОРРЕКТНЫМ поворотом на 180° вокруг середины M боковой стороны BC (формула P'=2M-P) - Раньше копия трапеции уходила за пределы viewBox (y=-20) - Теперь 4 шага: трапеция → поворот вокруг M → параллелограмм ABD'A' → половина = трапеция, формула S=½(a+b)h §8 Прямые углы: - Card 8.1: треугольник A(20,150) B(220,150) C(92,54) — НАСТОЯЩИЙ прямоугольный 3-4-5 с h_c=ab/c (раньше координаты не давали 90° в C) - Card 8.2: оба треугольника теперь корректные прямоугольные с прямыми углами на правильных вершинах - Card 8.3: треугольник 6-8-10, маркер прямого угла в H пересчитан через единичные векторы H→C и H→A (раньше показывал не то направление) Co-Authored-By: Claude Opus 4.7 (1M context) --- frontend/textbooks/geometry_8_ch2.html | 313 +++++++++++++++---------- 1 file changed, 183 insertions(+), 130 deletions(-) diff --git a/frontend/textbooks/geometry_8_ch2.html b/frontend/textbooks/geometry_8_ch2.html index a5c3440..a93e280 100644 --- a/frontend/textbooks/geometry_8_ch2.html +++ b/frontend/textbooks/geometry_8_ch2.html @@ -2201,50 +2201,48 @@ function buildP5(){ /* == INIT: Draggable трапеция == */ (function(){ - const W=400, H=260; - const AX=40, AY=200, BW=220; - let topB=100, topOff=40, trapH=110; + const W=420, H=280; + const AX=50, AY=220, BW=300; + let topB=140, topOff=80, trapH=130; function draw(){ const ax=AX, ay=AY, bx=ax+BW, by=ay; const dx=ax+topOff, dy=ay-trapH, cx=dx+topB, cy=dy; - const midX=(dx+cx)/2, midY=dy; - const Sval=Math.round((BW+topB)/2*trapH); - let s=''; - // height dashes - s+=''; - s+='h'; - // right angle - s+=''; - // trapezoid fill + const footX=Math.round((dx+cx)/2); + const Sval=Math.round((BW+topB)/2*trapH/10); + let s=''; s+=''; - // base a label - s+='a='+BW+''; - // top b label - s+='b='+topB+''; - // S label - const cxLabel=Math.round((ax+bx+cx+dx)/4), cyLabel=Math.round((ay+ay+cy+dy)/4); - s+='S='+Sval+''; - // drag handles - const hTop=cx, hTopY=cy; - s+=''; - s+=''; - const hH=ax, hHY=Math.round(ay-trapH/2); - s+=''; - s+=''; + s+=''; + s+=''; + s+=''; + ['A','B','C','D'].forEach((lbl,i)=>{ + const pt=[[ax,ay],[bx,by],[cx,cy],[dx,dy]][i]; + s+=''; + const off=[[ -10,14],[10,14],[10,-6],[-10,-6]][i]; + s+=''+lbl+''; + }); + s+='a = '+BW+''; + s+='b = '+topB+''; + s+='h = '+trapH+''; + s+='S = '+Sval+' усл.'; + s+=''; + s+=''; + s+=''; + s+=''; + s+='синий: тащи b'; + s+='жёлтый: тащи h'; s+=''; document.getElementById('p5-trap-svg-wrap').innerHTML=s; document.getElementById('p5-trap-info').innerHTML=`
Основание a
${BW}
Основание b
${topB}
Высота h
${trapH}
-
S = (a+b)/2·h
${Sval}
`; +
S = (a+b)·h/2
${Sval}
`; const handleTop=document.getElementById('p5-drag-top'); if(handleTop){ handleTop.addEventListener('pointerdown',ev=>{ const startX=ev.clientX, startB=topB; function onMove(e){ - const svgEl=document.getElementById('p5-trap-svg'); - if(!svgEl) return; + const svgEl=document.getElementById('p5-trap-svg'); if(!svgEl) return; const rect=svgEl.getBoundingClientRect(); const scale=W/rect.width; const delta=Math.round((e.clientX-startX)*scale); @@ -2262,12 +2260,11 @@ function buildP5(){ handleH.addEventListener('pointerdown',ev=>{ const startY=ev.clientY, startH=trapH; function onMove(e){ - const svgEl=document.getElementById('p5-trap-svg'); - if(!svgEl) return; + const svgEl=document.getElementById('p5-trap-svg'); if(!svgEl) return; const rect=svgEl.getBoundingClientRect(); const scale=H/rect.height; const delta=Math.round((startY-e.clientY)*scale); - trapH=Math.max(40,Math.min(160,startH+delta)); + trapH=Math.max(50,Math.min(180,startH+delta)); draw(); } function onUp(){window.removeEventListener('pointermove',onMove);window.removeEventListener('pointerup',onUp);window.removeEventListener('pointercancel',onUp);} @@ -2283,51 +2280,84 @@ function buildP5(){ /* == INIT: Доказательство пошаговое == */ (function(){ let step=0; - const W=400, H=220; - const ax=30, ay=180, bw=200, h=100, off=50; + const W=460, H=240; + /* Trapezoid ABCD: A,B bottom; C,D top. a=AB (bottom), b=DC (top). + Origin trapezoid: A(50,200), B(50+a,200), C(50+ofL+b,200-h), D(50+ofL,200-h). */ + const a=170, b=110, h=110, ofL=40, ofR=20; + const Ax=50, Ay=200; + const Bx=Ax+a, By=Ay; + const Dx=Ax+ofL, Dy=Ay-h; + const Cx=Dx+b, Cy=Dy; + /* Mid of right slanted side BC */ + const Mx=(Bx+Cx)/2, My=(By+Cy)/2; + /* Rotate 180° around M: P -> (2M-P) */ + const r=(p)=>({x:2*Mx-p.x, y:2*My-p.y}); + /* Rotated copy: A'=r(A), B'=r(B)=C, C'=r(C)=B, D'=r(D) */ + const Ap=r({x:Ax,y:Ay}), Dp=r({x:Dx,y:Dy}); + /* The combined parallelogram: A B D' A' D (going around). + Actually after rotation: + original sides: A→B→C→D→A + rotated copy sides: B→D'→A'→C(=B')→B + The union has outer boundary: A→B→D'→A'→D→A + which is a parallelogram with sides AB+BD'=(a+b) on bottom, A'D+DA on slants. */ + const heightFootX=Math.round((Dx+Cx)/2); + function vertexDots(pts){ + return pts.map(p=>``).join(''); + } + function vertexLabels(items){ + return items.map(([lbl,p,dx,dy,col])=>`${lbl}`).join(''); + } const steps=[ - {desc:'Исходная трапеция ABCD. Основания: $a=AB$ (нижнее) и $b=CD$ (верхнее), высота $h$.', + {desc:'Исходная трапеция $ABCD$: основания $a=AB$ (нижнее) и $b=DC$ (верхнее), высота $h$ — расстояние между основаниями.', draw(){ - const bx=ax+bw, dx=ax+off, cx=dx+Math.round(bw*0.55); - const dy=ay-h, cy=dy; - return '' - +'' - +'a' - +'b' - +'h' - +'A' - +'B' - +'C' - +'D'; + let s=''; + s+=``; + s+=``; + s+=``; + s+=`h`; + s+=`a`; + s+=`b`; + s+=vertexDots([{x:Ax,y:Ay},{x:Bx,y:By},{x:Cx,y:Cy},{x:Dx,y:Dy}]); + s+=vertexLabels([['A',{x:Ax,y:Ay},-12,14],['B',{x:Bx,y:By},6,14],['C',{x:Cx,y:Cy},6,-4],['D',{x:Dx,y:Dy},-12,-4]]); + return s; }}, - {desc:'Повернём копию трапеции на 180° и приложим сверху-справа так, чтобы стороны CD совпали.', + {desc:'Найдём середину $M$ боковой стороны $BC$. Повернём трапецию $ABCD$ на $180°$ вокруг точки $M$ — получим копию $A\'BCD\'$ (фиолетовая).', draw(){ - const bx=ax+bw, dx=ax+off, cx=dx+Math.round(bw*0.55); - const dy=ay-h, cy=dy; - const bw2=Math.round(bw*0.55); - const ax2=cx, ay2=cy, bx2=cx+bw, by2=cy, cx2=cx+bw-off, cy2=cy-h, dx2=cx, dy2=cy-h; - return '' - +'' - +'a' - +'копия (перевёрнута)'; + let s=''; + s+=``; + s+=``; + s+=``; + s+=`M`; + s+=vertexDots([{x:Ax,y:Ay},{x:Bx,y:By},{x:Cx,y:Cy},{x:Dx,y:Dy},Ap,Dp]); + s+=vertexLabels([ + ['A',{x:Ax,y:Ay},-12,14],['B',{x:Bx,y:By},4,14], + ['C',{x:Cx,y:Cy},-8,16],['D',{x:Dx,y:Dy},-12,-4], + ["D'",Dp,4,16,'#6d28d9'],["A'",Ap,6,-4,'#6d28d9'] + ]); + return s; }}, - {desc:'Две трапеции образуют параллелограмм! Его основание $a+b$, высота $h$.', + {desc:'Объединение двух трапеций — параллелограмм $ABD\'A\'$ со сторонами $AB+BD\' = a+b$ (внизу) и высотой $h$.', draw(){ - const bx=ax+bw, dx=ax+off, cx=dx+Math.round(bw*0.55); - const dy=ay-h, cy=dy; - const fullW=bw+Math.round(bw*0.55); - return '' - +'a+b' - +'h' - +'паралл.'; + let s=''; + s+=``; + s+=``; + s+=``; + s+=``; + s+=`h`; + s+=`a + b`; + s+=vertexDots([{x:Ax,y:Ay},{x:Bx,y:By},Dp,Ap]); + s+=vertexLabels([['A',{x:Ax,y:Ay},-12,14],['B',{x:Bx,y:By},-4,16],["D'",Dp,4,16,'#047857'],["A'",Ap,6,-4,'#047857']]); + return s; }}, - {desc:'Площадь параллелограмма $S_{\\text{пар}}=(a+b)\\cdot h$. Трапеция — его половина, значит $S=\\dfrac{(a+b)}{2}\\cdot h$.', + {desc:'$S_{\\text{пар}} = (a+b)\\cdot h$. Трапеция — ровно половина параллелограмма, поэтому $S_{\\text{трап}} = \\dfrac{a+b}{2}\\cdot h$.', draw(){ - const fullW=bw+Math.round(bw*0.55); - return '' - +'' - +'' - +'S=½(a+b)h'; + let s=''; + s+=``; + s+=``; + s+=``; + s+=`S = ½(a+b)·h`; + s+=vertexDots([{x:Ax,y:Ay},{x:Bx,y:By},{x:Cx,y:Cy},{x:Dx,y:Dy}]); + return s; }}, ]; function render(){ @@ -3425,30 +3455,34 @@ function buildP8(){
- - - - - - - - - - - - - + + + + + + + + + + + + + + + + - A - B - C - H - b - a - c - h_c - a_c - b_c + A + B + C + H + + b + a + h_c + a_c + b_c + катеты: a, b · гипотенуза: c = a_c + b_c
`); @@ -3460,25 +3494,35 @@ function buildP8(){

Приравниваем: $\\dfrac{1}{2} a b = \\dfrac{1}{2} c h_c$  ⇒  $h_c = \\dfrac{ab}{c}$. $\\square$

- - - - - - a - b - S=ab/2 - катеты a, b + + + + + + + a + b + S = ½ab + через катеты - = - - - - - h_c - c - гипотенуза c, h_c - S=ch_c/2 + = + + + + + + + + + + + c + h_c + S = ½ch_c + через гипотенузу и h_c
`); @@ -3487,27 +3531,36 @@ function buildP8(){

Проекции катетов $a_c=4$, $b_c=9$. Найти $h_c$.
$h_c=\\sqrt{4 \\cdot 9}=\\sqrt{36}=6$.

$h_c=12$, $a_c=9$. Найти $b_c$.
$b_c=\\dfrac{h_c^2}{a_c}=\\dfrac{144}{9}=16$.

- - - - - - - - - - - - + + + + + + + + + + + + + + + + - C - B - A - H - b=6 - a=8 - c=10 - h_c=4,8 + A + B + C + H + b=6 + a=8 + c=10 + h_c=4,8
`);