From 7fbbfad0fecc34fac97e64bea11a8c5855e82fa1 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 29 May 2026 09:16:52 +0300 Subject: [PATCH] =?UTF-8?q?fix(geom7=20ch4):=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B0=D0=BD=D1=8B=20=D1=80=D0=B8=D1=81=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D0=B8=20=C2=A721,=20=C2=A722,=20=C2=A725=20+=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=C2=A7?= =?UTF-8?q?24?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - §21: треугольник перестроен — цветовая кодировка (красная сторона = длиннейшая, зелёная = короткая) + углы напротив окрашены в тон стороне. Исправлена легенда (теперь корректно: c>a>b ⇒ ∠C>∠A>∠B). - §22: 'возможный' треугольник 4-5-6 с точными координатами вершины (решена система уравнений); 'невозможный' 3-4-8 показан как 2 дуги от A и B радиусов 3 и 4 (в масштабе 25px/ед.) с явным красным 'зазором'. - §24: добавлены 4 SVG-панели — по одной на каждый признак с цветовой подсветкой выделенных элементов (катеты / катет+угол / гипот+угол / гипот+катет). - §25: рисунок биссектрисы пересчитан по углу — стороны угла идут под углом ±25° от биссектрисы, K, F₁, F₂ вычисляются проекцией. Добавлены подписи d=d и одинаковые штрихи KF₁ = KF₂. --- frontend/textbooks/geometry_7_ch4.html | 204 ++++++++++++++++++------- 1 file changed, 149 insertions(+), 55 deletions(-) diff --git a/frontend/textbooks/geometry_7_ch4.html b/frontend/textbooks/geometry_7_ch4.html index 196cf4a..5caf990 100644 --- a/frontend/textbooks/geometry_7_ch4.html +++ b/frontend/textbooks/geometry_7_ch4.html @@ -769,17 +769,19 @@ function buildP21(){ let svg21=''; if(G){ - const b=G.svgBox(280,180,{id:'p21-tri',cell:20}); - /* Сделаем треугольник со сторонами разной длины */ - const A={x:30,y:150}, B={x:250,y:150}, C={x:90,y:30}; + const b=G.svgBox(300,180,{id:'p21-tri',cell:20}); + /* Скалянный треугольник: AB ≈ 240 (самая длинная), BC ≈ 184, AC ≈ 170 (самая короткая) + Тогда ∠C — самый большой (напротив AB), ∠B — самый маленький (напротив AC) */ + const A={x:30,y:160}, B={x:270,y:160}, C={x:140,y:30}; svg21 = b.open + G.polygon([A,B,C],{color:'#0891b2',fill:'rgba(8,145,178,.08)'}) - + G.segment(A,B,{color:'#dc2626',width:2.5,label:'c (длинная)',labelOffset:18}) - + G.segment(A,C,{color:'#059669',width:2.5,label:'b',labelOffset:-16}) - + G.segment(B,C,{color:'#7c3aed',width:2.5,label:'a',labelOffset:-16}) - + G.angle(B,A,C,{color:'#dc2626',r:24,label:'∠B (бол.)',fontSize:11,labelOffset:14}) - + G.angle(A,B,C,{color:'#7c3aed',r:24,label:'∠A',fontSize:11,labelOffset:12}) - + G.angle(C,A,B,{color:'#059669',r:22,label:'∠C',fontSize:11,labelOffset:11}) + /* Цветовая кодировка: красный = самый большой, оранж. = средний, зелёный = самый маленький */ + + G.segment(A,B,{color:'#dc2626',width:3,label:'c — самая длин.',labelOffset:18,labelColor:'#dc2626'}) + + G.segment(B,C,{color:'#f59e0b',width:2.5,label:'a',labelOffset:-16,labelColor:'#f59e0b'}) + + G.segment(A,C,{color:'#059669',width:2,label:'b — самая корот.',labelOffset:-16,labelColor:'#059669'}) + + G.angle(C,A,B,{color:'#dc2626',r:28,label:'∠C — наиб.',fontSize:11,labelOffset:14}) + + G.angle(A,B,C,{color:'#f59e0b',r:22,label:'∠A',fontSize:11,labelOffset:12}) + + G.angle(B,A,C,{color:'#059669',r:18,label:'∠B — наим.',fontSize:11,labelOffset:12}) + G.point(A.x,A.y,'A',{color:'#1e293b',dx:-14,dy:12}) + G.point(B.x,B.y,'B',{color:'#1e293b',dx:8,dy:12}) + G.point(C.x,C.y,'C',{color:'#1e293b',dx:-4,dy:-8}) @@ -789,8 +791,9 @@ function buildP21(){ html += makeCard('rule', 'Прямое свойство', '21.1', `

Теорема. В треугольнике против бо́льшей стороны лежит бо́льший угол.

`+svg21+`
-

На рисунке: $AB > AC > BC$ $\\Rightarrow$ $\\angle C > \\angle B > \\angle A$.

-

Правило большого пальца. Самая длинная сторона смотрит на самый большой угол.

`); +

На рисунке: $AB > BC > AC$ (стороны $c > a > b$) $\\Rightarrow$ $\\angle C > \\angle A > \\angle B$.

+

Цвет = размер. Красным выделены самая длинная сторона и самый большой угол напротив неё. Зелёным — самая короткая и самый маленький.

+

Правило большого пальца. Самая длинная сторона «смотрит» на самый большой угол.

`); html += makeCard('rule', 'Обратное свойство', '21.2', `

Теорема (обратная). Против бо́льшего угла лежит бо́льшая сторона.

@@ -881,29 +884,50 @@ function buildP22(){ let svgOK='', svgBAD=''; if(G){ - /* Валидный треугольник со сторонами 5, 6, 7 */ - const b1=G.svgBox(220,150,{id:'p22-ok',cell:20}); - const A={x:30,y:120}, B={x:180,y:120}, C={x:100,y:40}; + /* === Валидный треугольник 4, 5, 6 === + Масштаб: 1 ед = 25 px. AB = 6 ед = 150 px (база) + Решая систему AC=5*25=125, BC=4*25=100 от A={20,130}, B={170,130}: + C ≈ {114, 47} */ + const b1=G.svgBox(220,160,{id:'p22-ok',cell:20}); + const A={x:20,y:130}, B={x:170,y:130}, C={x:114,y:47}; svgOK = b1.open - + G.polygon([A,B,C],{color:'#059669',fill:'rgba(16,185,129,.10)'}) - + G.segment(A,B,{color:'#059669',width:2,label:'7',labelOffset:14}) - + G.segment(A,C,{color:'#059669',width:2,label:'5',labelOffset:-14}) - + G.segment(B,C,{color:'#059669',width:2,label:'6',labelOffset:-14}) - + '5+6 > 7 ✓' + + G.polygon([A,B,C],{color:'#059669',width:2.5,fill:'rgba(16,185,129,.12)'}) + + G.segment(A,B,{color:'#065f46',width:2,label:'6',labelOffset:14,labelColor:'#065f46'}) + + G.segment(A,C,{color:'#065f46',width:2,label:'5',labelOffset:-14,labelColor:'#065f46'}) + + G.segment(B,C,{color:'#065f46',width:2,label:'4',labelOffset:-14,labelColor:'#065f46'}) + + G.point(A.x,A.y,'A',{color:'#1e293b',dx:-12,dy:12,fontSize:11}) + + G.point(B.x,B.y,'B',{color:'#1e293b',dx:6,dy:12,fontSize:11}) + + G.point(C.x,C.y,'C',{color:'#1e293b',dx:-4,dy:-8,fontSize:11}) + + '' + + '4 + 5 > 6 ✓ существует' + b1.close; - /* Невалидный «треугольник» 3, 4, 8: AB = 8 длинная, AC=3, BC=4 — не сходится */ - const b2=G.svgBox(220,150,{id:'p22-bad',cell:20}); - const A2={x:20,y:120}, B2={x:200,y:120}; - /* C "висит" — две дуги не пересекаются */ + + /* === Невалидный: 3, 4, 8 === + Масштаб: 1 ед = 25 px. AB = 8*25 = 200 px + Дуга от A: r = 3*25 = 75 (всё, до чего достаёт «3-я сторона») + Дуга от B: r = 4*25 = 100 (всё, до чего достаёт «4-я сторона») + Зазор = 200 - 75 - 100 = 25 px → дуги не пересекаются */ + const b2=G.svgBox(240,160,{id:'p22-bad',cell:20}); + const A2={x:20,y:130}, B2={x:220,y:130}; + /* Дуги сверху над прямой AB */ svgBAD = b2.open - + G.segment(A2,B2,{color:'#dc2626',width:2,label:'8',labelOffset:14}) - + G.arc(A2,30,-Math.PI*0.85,-Math.PI*0.15,{color:'#7c3aed',width:1.5,dash:'3 2'}) - + G.arc(B2,40,Math.PI*1.15,Math.PI*1.85,{color:'#f59e0b',width:1.5,dash:'3 2'}) - + 'r=3' - + 'r=4' - + G.point(A2.x,A2.y,'A',{color:'#1e293b',dx:-14,dy:12}) - + G.point(B2.x,B2.y,'B',{color:'#1e293b',dx:8,dy:12}) - + '3+4 < 8 ✗' + + G.segment(A2,B2,{color:'#dc2626',width:2.5,label:'8',labelOffset:14,labelColor:'#7f1d1d'}) + /* Дуга от A (радиус 75, верхняя полуокружность) */ + + '' + /* Дуга от B (радиус 100, верхняя полуокружность) */ + + '' + /* Линии-радиусы (показать длины 3 и 4) */ + + G.segment(A2,{x:A2.x,y:A2.y-75},{color:'#7c3aed',width:1.2}) + + G.segment(B2,{x:B2.x,y:B2.y-100},{color:'#f59e0b',width:1.2}) + + '3' + + '4' + /* Двойная стрелка «зазор» */ + + '' + + 'зазор' + + G.point(A2.x,A2.y,'A',{color:'#1e293b',dx:-12,dy:14,fontSize:11}) + + G.point(B2.x,B2.y,'B',{color:'#1e293b',dx:6,dy:14,fontSize:11}) + + '' + + '3 + 4 < 8 ✗ не существует' + b2.close; } @@ -1090,8 +1114,54 @@ function buildP24(){ const G = window.GEOM7; let html = ''; + /* === Хелпер: рисует пару прямоугольных треугольников с подсветкой === + mark — какие элементы выделены: 'legs', 'leg-angle', 'hyp-angle', 'hyp-leg' */ + function drawRTPair(id, mark, title){ + if(!G) return ''; + const b = G.svgBox(220, 130, {id:id, cell:20, bg:'#fff'}); + /* Левый треугольник: C={20,100}, A={20,30}, B={100,100} */ + /* Правый треугольник: C2={120,100}, A2={120,30}, B2={200,100} */ + const C1={x:20,y:100}, A1={x:20,y:30}, B1={x:100,y:100}; + const C2={x:130,y:100}, A2={x:130,y:30}, B2={x:210,y:100}; + function tri(C,A,B,opts){ + opts = opts || {}; + let s = G.polygon([A,B,C],{color:'#0891b2',fill:'rgba(8,145,178,.08)',width:1.8}); + s += G.rightAngleMark(C,A,B,{color:'#475569',size:9}); + /* Катет 1 (вертикальный CA) */ + const c1Color = (mark==='legs' || mark==='leg-angle' || mark==='hyp-leg') ? '#dc2626' : '#475569'; + const c1Tick = (mark==='legs' || mark==='leg-angle' || mark==='hyp-leg') ? 1 : 0; + s += G.segment(C,A,{color:c1Color,width:2.5,ticks:c1Tick,tickLen:5}); + /* Катет 2 (горизонтальный CB) */ + const c2Color = (mark==='legs') ? '#7c3aed' : '#475569'; + const c2Tick = (mark==='legs') ? 2 : 0; + s += G.segment(C,B,{color:c2Color,width:2.5,ticks:c2Tick,tickLen:5}); + /* Гипотенуза AB */ + const hColor = (mark==='hyp-angle' || mark==='hyp-leg') ? '#dc2626' : '#475569'; + const hTick = (mark==='hyp-angle' || mark==='hyp-leg') ? 1 : 0; + s += G.segment(A,B,{color:hColor,width:2.5,ticks:hTick,tickLen:5}); + /* Острый угол при A */ + if(mark==='leg-angle' || mark==='hyp-angle'){ + s += G.angle(A,B,C,{color:'#f59e0b',r:20,width:2}); + /* Маленький маркер дуги: 1 штрих */ + s += ''; + } + return s; + } + let s = b.open + tri(C1,A1,B1) + tri(C2,A2,B2) + + '△ 1' + + '△ 2' + + '=' + + b.close; + return '
'+title+'
'+s+'
'; + } + const svgP1 = drawRTPair('p24-pr1', 'legs', 'Признак 1 · 2 катета'); + const svgP2 = drawRTPair('p24-pr2', 'leg-angle', 'Признак 2 · катет + угол'); + const svgP3 = drawRTPair('p24-pr3', 'hyp-angle', 'Признак 3 · гипот. + угол'); + const svgP4 = drawRTPair('p24-pr4', 'hyp-leg', 'Признак 4 · гипот. + катет'); + html += makeCard('rule', '4 признака равенства прямоугольных $\\triangle$', '24.1', ` -

Для прямоугольных треугольников дополнительно к 3 признакам обычных треугольников действуют упрощённые формулировки.

+

Для прямоугольных треугольников дополнительно к 3 признакам обычных треугольников действуют упрощённые формулировки. Цвет = выделенный элемент.

+
`+svgP1+svgP2+svgP3+svgP4+`

Признак 1. По двум катетам. Если 2 катета одного $\\triangle$ равны 2 катетам другого — треугольники равны. (По 1-му признаку обычных $\\triangle$: 2 стороны + угол $90°$ между ними.)

Признак 2. По катету и прилежащему острому углу. Если катет и прилежащий к нему острый угол одного $\\triangle$ равны катету и прилежащему углу другого — треугольники равны.

Признак 3. По гипотенузе и острому углу. Если гипотенуза и острый угол одного $\\triangle$ равны гипотенузе и острому углу другого — треугольники равны.

@@ -1188,37 +1258,61 @@ function buildP25(){ let svgBis=''; if(G){ - const b=G.svgBox(280,180,{id:'p25-bis',cell:20}); - const O={x:60,y:140}; - const A={x:240,y:60}; /* верхняя сторона */ - const B={x:260,y:160}; /* нижняя сторона */ - /* Биссектриса — между A и B */ - const Bs={x:260,y:108}; + /* Чистый рисунок: вершина угла O слева, биссектриса вправо вдоль оси x. + Угол ∠AOB = 50° (по 25° от биссектрисы вверх и вниз). */ + const b=G.svgBox(300,200,{id:'p25-bis',cell:20}); + const O={x:40,y:100}; + const ang=25*Math.PI/180; /* половина угла */ + const len=220; + /* OA — верхняя сторона: направление (cos(-25°), sin(-25°)) */ + const Aend={x:O.x+len*Math.cos(-ang), y:O.y+len*Math.sin(-ang)}; + /* OB — нижняя сторона */ + const Bend={x:O.x+len*Math.cos(ang), y:O.y+len*Math.sin(ang)}; + /* Биссектриса — горизонталь от O */ + const Bs={x:O.x+len,y:O.y}; /* Точка K на биссектрисе */ - const K={x:160,y:110}; - /* Перпендикуляры из K к сторонам */ - /* Сторона OA: вектор */ + const K={x:O.x+150,y:O.y}; + /* F1, F2 — основания перпендикуляров из K на OA и OB */ function foot(P, P1, P2){ const v={x:P2.x-P1.x, y:P2.y-P1.y}; const w={x:P.x-P1.x, y:P.y-P1.y}; const t=(v.x*w.x+v.y*w.y)/(v.x*v.x+v.y*v.y); return {x:P1.x+t*v.x, y:P1.y+t*v.y}; } - const F1=foot(K,O,A); - const F2=foot(K,O,B); + const F1=foot(K,O,Aend); + const F2=foot(K,O,Bend); svgBis = b.open - + G.segment(O,A,{color:'#7c3aed',width:2.5}) - + G.segment(O,B,{color:'#7c3aed',width:2.5}) - + G.segment(O,Bs,{color:'#dc2626',width:2,dash:'5 3'}) - + G.segment(K,F1,{color:'#0891b2',width:1.5,dash:'3 2'}) - + G.segment(K,F2,{color:'#0891b2',width:1.5,dash:'3 2'}) - + G.rightAngleMark(F1,K,O,{color:'#0891b2',size:8}) - + G.rightAngleMark(F2,K,O,{color:'#0891b2',size:8}) - + G.point(O.x,O.y,'O',{color:'#1e293b',dx:-12,dy:14}) - + G.point(K.x,K.y,'K',{color:'#dc2626',dx:6,dy:-6,r:3.5}) - + G.point(F1.x,F1.y,'',{r:2.5,color:'#0891b2'}) - + G.point(F2.x,F2.y,'',{r:2.5,color:'#0891b2'}) - + 'бис.' + /* Стороны угла */ + + G.segment(O,Aend,{color:'#7c3aed',width:2.5}) + + G.segment(O,Bend,{color:'#7c3aed',width:2.5}) + /* Биссектриса */ + + G.segment(O,Bs,{color:'#dc2626',width:2,dash:'6 3'}) + /* Дуга, показывающая равенство углов (две одинаковые) */ + + G.arc(O,28,-ang,0,{color:'#dc2626',width:1.5}) + + G.arc(O,28,0,ang,{color:'#dc2626',width:1.5}) + /* Перпендикуляры от K */ + + G.segment(K,F1,{color:'#0891b2',width:2}) + + G.segment(K,F2,{color:'#0891b2',width:2}) + /* Прямые углы у оснований */ + + G.rightAngleMark(F1,K,O,{color:'#0891b2',size:10}) + + G.rightAngleMark(F2,K,O,{color:'#0891b2',size:10}) + /* Метки KF1 = KF2 — тики на перпендикулярах */ + + G.segment(K,F1,{color:'#0891b2',width:0.01,ticks:1,tickLen:5}) + + G.segment(K,F2,{color:'#0891b2',width:0.01,ticks:1,tickLen:5}) + /* Точки */ + + G.point(O.x,O.y,'O',{color:'#1e293b',dx:-14,dy:5,fontSize:13}) + + G.point(K.x,K.y,'K',{color:'#dc2626',dx:-10,dy:-8,fontSize:13,r:3.5}) + + G.point(Bs.x,Bs.y,'',{r:2,color:'#dc2626'}) + + G.point(F1.x,F1.y,'F₁',{color:'#0891b2',dx:6,dy:-4,fontSize:11,r:3}) + + G.point(F2.x,F2.y,'F₂',{color:'#0891b2',dx:6,dy:12,fontSize:11,r:3}) + /* Подписи */ + + 'бис.' + + 'A' + + 'B' + /* Равенство расстояний */ + + 'd' + + 'd' + + 'KF₁ = KF₂ = d' + b.close; }