From ba4c9b071ddc09ec92f31e330b9bdfb087a03111 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 29 May 2026 10:51:02 +0300 Subject: [PATCH] =?UTF-8?q?fix(geom9=20ch1):=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=D0=B2=20=C2=A71=20=D0=B8=20=C2=A73?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §1 IV1 «Конструктор прямоугольного треугольника»: - Стандартное расположение: прямой угол справа-снизу (C), угол α при A слева-снизу, гипотенуза диагональю - Цветовая кодировка сторон: гипотенуза c фиолетовая, противолежащий a красный, прилежащий b синий - Подписи в реальных единицах (c = 10), а не px/22 - Легенда с обозначением каждой стороны - Под графиком — формулы $\sin = a/c$, $\cos = b/c$ итд §3 IV1 «Два эталонных треугольника» (бывшая «Три»): - Поправлен заголовок: было «Три», нарисовано два - Оба треугольника в стандартном расположении - Помощник drawTri() — единая логика для обоих - Углы 30°/60° (красный/голубой) для 30-60-90, 45°/45° для равнобедренного Co-Authored-By: Claude Haiku 4.5 --- frontend/textbooks/geometry_9_ch1.html | 245 +++++++++++++------------ 1 file changed, 124 insertions(+), 121 deletions(-) diff --git a/frontend/textbooks/geometry_9_ch1.html b/frontend/textbooks/geometry_9_ch1.html index 25a986b..18b4a25 100644 --- a/frontend/textbooks/geometry_9_ch1.html +++ b/frontend/textbooks/geometry_9_ch1.html @@ -644,69 +644,77 @@ function buildP1(){ box.innerHTML = html; renderMath(box); - /* IV1 — слайдер + SVG */ + /* IV1 — слайдер + SVG (стандартное расположение: прямой угол справа-снизу, угол α при A) */ (function(){ const sl = document.getElementById('p1-iv1-a'); const lab = document.getElementById('p1-iv1-aval'); const svg = document.getElementById('p1-iv1-svg'); const out = document.getElementById('p1-iv1-out'); const seen = new Set(); + // Цвета сторон: гипотенуза — фиолетовая, противолежащий α — красный, прилежащий — синий. + const COL_HYP = '#7c3aed', COL_OPP = '#dc2626', COL_ADJ = '#2563eb'; function draw(){ const aDeg = +sl.value; lab.textContent = aDeg; const aRad = deg2rad(aDeg); - const c = 220; // гипотенуза в пикселях - // Геометрические вершины: A (внизу слева), B (верх — прямой угол), C (внизу справа). - // Прямой угол при B. Угол α при C. - // BC = c·cos α (горизонтальный катет, прилежащий к α) - // AB = c·sin α (вертикальный катет, противолежащий α) - const BCpx = c * Math.cos(aRad); - const ABpx = c * Math.sin(aRad); - const cx = 60, cyBase = 270; // позиция A - const A = {x: cx, y: cyBase}; - const C = {x: cx + BCpx, y: cyBase}; - const B = {x: cx, y: cyBase - ABpx}; - // Внутренние векторы в B (прямой угол): по BA — вниз, по BC — вправо-вниз - const uBA = unitVec(B, A); - const uBC = unitVec(B, C); - const uCA = unitVec(C, A); // вдоль гипотенузы из C - const uCB = unitVec(C, B); // вдоль катета из C + // Целые «единицы» для подписи: гипотенуза c = 10. + const cUnits = 10; + const aUnits = +(cUnits * Math.sin(aRad)).toFixed(2); // противолежащий α + const bUnits = +(cUnits * Math.cos(aRad)).toFixed(2); // прилежащий α + // Масштаб px на единицу подбираем так, чтобы прилежащий катет вписался в ~300 px. + const px = 26; + // Стандартное расположение: + // A — нижний левый угол (вершина с углом α). + // C — нижний правый угол (прямой угол). + // B — верхний правый угол. + // Гипотенуза = AB (диагональ), прилежащий к α = AC (горизонталь), противолежащий = BC (вертикаль). + const A = {x: 60, y: 280}; + const C = {x: 60 + bUnits * px, y: 280}; + const B = {x: C.x, y: 280 - aUnits * px}; + // Векторы внутрь треугольника + const uCA = unitVec(C, A), uCB = unitVec(C, B); // прямой угол в C + const uAC = unitVec(A, C), uAB = unitVec(A, B); // острый α в A let s = ''; - // фон - s += ''; - // треугольник - s += ''; - // маркер прямого угла в B (внутренние векторы — uBA и uBC) - s += ''; - // дуга угла α при C (от CA к CB) - s += ''; + // Стороны как отдельные линии разных цветов + s += ''; + s += ''; + s += ''; + s += ''; + // маркер прямого угла в C + s += ''; + // дуга угла α при A + s += ''; // подпись α - const aMid = {x: C.x + 44*Math.cos(Math.atan2((uCA.y+uCB.y)/2,(uCA.x+uCB.x)/2)), y: C.y + 44*Math.sin(Math.atan2((uCA.y+uCB.y)/2,(uCA.x+uCB.x)/2))}; - s += 'α'; + const midDir = {x:(uAC.x+uAB.x)/2, y:(uAC.y+uAB.y)/2}; + const midLen = Math.sqrt(midDir.x*midDir.x+midDir.y*midDir.y) || 1; + const aLab = {x: A.x + 50*midDir.x/midLen, y: A.y + 50*midDir.y/midLen}; + s += 'α'; // вершины - s += ''; - s += ''; - s += ''; - s += 'A'; - s += 'B'; - s += 'C'; - // подписи сторон - const labBC = 'BC='+(BCpx/22).toFixed(2); - const labAB = 'AB='+(ABpx/22).toFixed(2); - const labAC = 'AC='+(c/22).toFixed(2); - s += ''+labBC+''; - s += ''+labAB+''; - // подпись гипотенузы — поднимем над линией AC - const midAC = {x:(A.x+C.x)/2, y:(A.y+C.y)/2}; - const nAC = {x:-(C.y-A.y), y:(C.x-A.x)}; - const nL = Math.sqrt(nAC.x*nAC.x+nAC.y*nAC.y)||1; - const labP = {x: midAC.x + 16*nAC.x/nL, y: midAC.y + 16*nAC.y/nL}; - s += ''+labAC+''; + [A,B,C].forEach(P=>{ s+=''; }); + s += 'A'; + s += 'B'; + s += 'C'; + // длина прилежащего катета AC + s += 'b = '+bUnits.toFixed(2)+''; + // длина противолежащего BC + s += 'a = '+aUnits.toFixed(2)+''; + // длина гипотенузы AB — над линией + const midAB = {x:(A.x+B.x)/2, y:(A.y+B.y)/2}; + const nAB = {x:-(B.y-A.y), y:(B.x-A.x)}; + const nL = Math.sqrt(nAB.x*nAB.x+nAB.y*nAB.y)||1; + const labHyp = {x: midAB.x - 22*nAB.x/nL, y: midAB.y - 22*nAB.y/nL}; + s += 'c = '+cUnits+''; + // легенда + s += ''; + s += 'гипотенуза c'; + s += 'противолежащий a'; + s += 'прилежащий b'; + s += ''; svg.innerHTML = s; // числовые значения const sn = Math.sin(aRad), cs = Math.cos(aRad), tn = Math.tan(aRad), ct = 1/Math.tan(aRad); - out.innerHTML = '$\\sin '+aDeg+'^\\circ \\approx '+sn.toFixed(3)+'$  ·  $\\cos '+aDeg+'^\\circ \\approx '+cs.toFixed(3)+'$
' - + '$\\tan '+aDeg+'^\\circ \\approx '+tn.toFixed(3)+'$  ·  $\\cot '+aDeg+'^\\circ \\approx '+ct.toFixed(3)+'$'; + out.innerHTML = '$\\sin '+aDeg+'^\\circ = \\dfrac{a}{c} \\approx '+sn.toFixed(3)+'$  ·  $\\cos '+aDeg+'^\\circ = \\dfrac{b}{c} \\approx '+cs.toFixed(3)+'$
' + + '$\\tan '+aDeg+'^\\circ = \\dfrac{a}{b} \\approx '+tn.toFixed(3)+'$  ·  $\\cot '+aDeg+'^\\circ = \\dfrac{b}{a} \\approx '+ct.toFixed(3)+'$'; renderMath(out); seen.add(aDeg); if(seen.size >= 6 && !seen.has('done')){ addXp(10,'p1-iv1'); bumpProgress('p1', 15); seen.add('done'); } @@ -1231,8 +1239,8 @@ function buildP3(){ /* IV1 — Три эталонных треугольника */ html += `
-
ИНТЕРАКТИВ 1
Три эталонных треугольника
-
Слева — треугольник $30^\\circ\\text{-}60^\\circ\\text{-}90^\\circ$ (катеты $1$ и $\\sqrt{3}$, гипотенуза $2$). Справа — равнобедренный $45^\\circ\\text{-}45^\\circ\\text{-}90^\\circ$ (катеты $1$, гипотенуза $\\sqrt{2}$).
+
ИНТЕРАКТИВ 1
Два эталонных треугольника
+
Слева — треугольник $30^\\circ\\text{-}60^\\circ\\text{-}90^\\circ$ (катеты $1$ и $\\sqrt{3}$, гипотенуза $2$): из него читаются значения для $30^\\circ$ и $60^\\circ$. Справа — равнобедренный $45^\\circ\\text{-}45^\\circ\\text{-}90^\\circ$ (катеты $1$, гипотенуза $\\sqrt{2}$): из него — значения для $45^\\circ$.
@@ -1304,82 +1312,77 @@ function buildP3(){ box.innerHTML = html; renderMath(box); - /* IV1 — рисуем три эталонных треугольника */ + /* IV1 — Два эталонных треугольника (стандартное расположение: прямой угол справа-снизу) */ (function(){ const svg = document.getElementById('p3-iv1-svg'); if(!svg) return; - let s = ''; - // Треугольник 30-60-90: катеты 1 и sqrt(3), гипотенуза 2. Используем масштаб 60 px = 1 ед. - // Левый: BC = sqrt(3) (горизонт., прилежащий 30°), AB = 1 (вертикальный, противолежащий 30°) - // Вершины: A слева внизу, B слева вверху (прямой угол), C справа внизу. - // Угол 30° при C, угол 60° при A. - (function(){ - const u = 60; - const Ax = 40, Ay = 180; - const A = {x: Ax, y: Ay}; - const B = {x: Ax, y: Ay - u}; // катет AB = 1 - const C = {x: Ax + Math.sqrt(3)*u, y: Ay}; // катет BC = sqrt(3) - const uBA = unitVec(B, A); - const uBC = unitVec(B, C); - const uCA = unitVec(C, A); - const uCB = unitVec(C, B); - const uAB = unitVec(A, B); - const uAC = unitVec(A, C); - s += ''; - s += ''; - s += ''; - s += ''; - // подписи углов - s += '30°'; - s += '60°'; - // вершины и подписи - ['A','B','C'].forEach((nm,i)=>{ const P=[A,B,C][i]; s+=''; }); - s += 'A'; - s += 'B'; - s += 'C'; - // длины сторон - s += '1'; - s += '√3'; - const midAC={x:(A.x+C.x)/2, y:(A.y+C.y)/2}; - const nAC={x:-(C.y-A.y), y:(C.x-A.x)}; - const nL=Math.sqrt(nAC.x*nAC.x+nAC.y*nAC.y)||1; - const labP={x: midAC.x + 16*nAC.x/nL, y: midAC.y + 16*nAC.y/nL}; - s += '2'; + // Стандартная схема для обоих треугольников: + // A — нижний левый угол (острый, на нём показываем основной интересующий нас угол). + // C — нижний правый угол (прямой). + // B — верхний правый угол (второй острый). + // Прилежащий катет = AC (горизонтальный), противолежащий = BC (вертикальный), гипотенуза = AB. + function drawTri(opts){ + const { Ax, Ay, b, a, angAname, angBname, sideAdjLab, sideOppLab, hypLab, title } = opts; + const A = {x: Ax, y: Ay}; + const C = {x: Ax + b, y: Ay}; + const B = {x: C.x, y: Ay - a}; + const uCA = unitVec(C, A), uCB = unitVec(C, B); + const uAC = unitVec(A, C), uAB = unitVec(A, B); + const uBA = unitVec(B, A), uBC = unitVec(B, C); + let s = ''; + // заполнение и контур треугольника + s += ''; + // маркер прямого угла в C + s += ''; + // дуга и подпись угла при A (основной) + s += ''; + const midA = {x:(uAC.x+uAB.x)/2, y:(uAC.y+uAB.y)/2}; + const mAlen = Math.sqrt(midA.x*midA.x+midA.y*midA.y)||1; + s += ''+angAname+''; + // дуга и подпись угла при B + s += ''; + const midB = {x:(uBA.x+uBC.x)/2, y:(uBA.y+uBC.y)/2}; + const mBlen = Math.sqrt(midB.x*midB.x+midB.y*midB.y)||1; + s += ''+angBname+''; + // вершины + [A,B,C].forEach(P=>{ s+=''; }); + s += 'A'; + s += 'B'; + s += 'C'; + // длина прилежащего AC + s += ''+sideAdjLab+''; + // длина противолежащего BC + s += ''+sideOppLab+''; + // длина гипотенузы AB — над линией + const midAB = {x:(A.x+B.x)/2, y:(A.y+B.y)/2}; + const nAB = {x:-(B.y-A.y), y:(B.x-A.x)}; + const nL = Math.sqrt(nAB.x*nAB.x+nAB.y*nAB.y)||1; + const labP = {x: midAB.x - 18*nAB.x/nL, y: midAB.y - 18*nAB.y/nL}; + s += ''+hypLab+''; // заголовок - s += '30\xb0-60\xb0-90\xb0'; - })(); - // Треугольник 45-45-90: оба катета 1, гипотенуза sqrt(2). Масштаб тот же. - (function(){ - const u = 90; // покрупнее - const Ax = 360, Ay = 180; - const A = {x: Ax, y: Ay}; - const B = {x: Ax, y: Ay - u}; - const C = {x: Ax + u, y: Ay}; - const uBA = unitVec(B, A); - const uBC = unitVec(B, C); - const uCA = unitVec(C, A); - const uCB = unitVec(C, B); - const uAB = unitVec(A, B); - const uAC = unitVec(A, C); - s += ''; - s += ''; - s += ''; - s += ''; - s += '45°'; - s += '45°'; - ['A','B','C'].forEach((nm,i)=>{ const P=[A,B,C][i]; s+=''; }); - s += 'A'; - s += 'B'; - s += 'C'; - s += '1'; - s += '1'; - const midAC={x:(A.x+C.x)/2, y:(A.y+C.y)/2}; - const nAC={x:-(C.y-A.y), y:(C.x-A.x)}; - const nL=Math.sqrt(nAC.x*nAC.x+nAC.y*nAC.y)||1; - const labP={x: midAC.x + 16*nAC.x/nL, y: midAC.y + 16*nAC.y/nL}; - s += '√2'; - s += '45\xb0-45\xb0-90\xb0'; - })(); + s += ''+title+''; + return s; + } + let s = ''; + // 30-60-90 (слева): катет AC = √3 (прилежащий 30°), катет BC = 1 (противолежащий 30°), гипотенуза AB = 2. + // Острый угол A = 30°, острый B = 60°. + s += drawTri({ + Ax: 30, Ay: 180, + b: Math.sqrt(3)*70, // прилежащий к 30° (длинный) + a: 70, // противолежащий 30° (короткий) + angAname: '30°', angBname: '60°', + sideAdjLab: '√3', sideOppLab: '1', hypLab: '2', + title: '30°–60°–90°' + }); + // 45-45-90 (справа): оба катета = 1, гипотенуза = √2. + s += drawTri({ + Ax: 380, Ay: 180, + b: 95, + a: 95, + angAname: '45°', angBname: '45°', + sideAdjLab: '1', sideOppLab: '1', hypLab: '√2', + title: '45°–45°–90°' + }); svg.innerHTML = s; addXp(10,'p3-iv1'); bumpProgress('p3', 15); })();