fix(geom9 ch1): переделаны рисунки в §1 и §3

§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 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-29 10:51:02 +03:00
parent a4c933b62c
commit ba4c9b071d
+124 -121
View File
@@ -644,69 +644,77 @@ function buildP1(){
box.innerHTML = html; box.innerHTML = html;
renderMath(box); renderMath(box);
/* IV1 — слайдер + SVG */ /* IV1 — слайдер + SVG (стандартное расположение: прямой угол справа-снизу, угол α при A) */
(function(){ (function(){
const sl = document.getElementById('p1-iv1-a'); const sl = document.getElementById('p1-iv1-a');
const lab = document.getElementById('p1-iv1-aval'); const lab = document.getElementById('p1-iv1-aval');
const svg = document.getElementById('p1-iv1-svg'); const svg = document.getElementById('p1-iv1-svg');
const out = document.getElementById('p1-iv1-out'); const out = document.getElementById('p1-iv1-out');
const seen = new Set(); const seen = new Set();
// Цвета сторон: гипотенуза — фиолетовая, противолежащий α — красный, прилежащий — синий.
const COL_HYP = '#7c3aed', COL_OPP = '#dc2626', COL_ADJ = '#2563eb';
function draw(){ function draw(){
const aDeg = +sl.value; const aDeg = +sl.value;
lab.textContent = aDeg; lab.textContent = aDeg;
const aRad = deg2rad(aDeg); const aRad = deg2rad(aDeg);
const c = 220; // гипотенуза в пикселях // Целые «единицы» для подписи: гипотенуза c = 10.
// Геометрические вершины: A (внизу слева), B (верх — прямой угол), C (внизу справа). const cUnits = 10;
// Прямой угол при B. Угол α при C. const aUnits = +(cUnits * Math.sin(aRad)).toFixed(2); // противолежащий α
// BC = c·cos α (горизонтальный катет, прилежащий к α) const bUnits = +(cUnits * Math.cos(aRad)).toFixed(2); // прилежащий α
// AB = c·sin α (вертикальный катет, противолежащий α) // Масштаб px на единицу подбираем так, чтобы прилежащий катет вписался в ~300 px.
const BCpx = c * Math.cos(aRad); const px = 26;
const ABpx = c * Math.sin(aRad); // Стандартное расположение:
const cx = 60, cyBase = 270; // позиция A // A — нижний левый угол (вершина с углом α).
const A = {x: cx, y: cyBase}; // C — нижний правый угол (прямой угол).
const C = {x: cx + BCpx, y: cyBase}; // B — верхний правый угол.
const B = {x: cx, y: cyBase - ABpx}; // Гипотенуза = AB (диагональ), прилежащий к α = AC (горизонталь), противолежащий = BC (вертикаль).
// Внутренние векторы в B (прямой угол): по BA — вниз, по BC — вправо-вниз const A = {x: 60, y: 280};
const uBA = unitVec(B, A); const C = {x: 60 + bUnits * px, y: 280};
const uBC = unitVec(B, C); const B = {x: C.x, y: 280 - aUnits * px};
const uCA = unitVec(C, A); // вдоль гипотенузы из C // Векторы внутрь треугольника
const uCB = unitVec(C, B); // вдоль катета из C const uCA = unitVec(C, A), uCB = unitVec(C, B); // прямой угол в C
const uAC = unitVec(A, C), uAB = unitVec(A, B); // острый α в A
let s = ''; let s = '';
// фон // Стороны как отдельные линии разных цветов
s += '<rect x="0" y="0" width="400" height="320" fill="none"/>'; s += '<polygon points="'+A.x+','+A.y+' '+C.x+','+C.y+' '+B.x+','+B.y+'" fill="rgba(217,119,6,.07)" stroke="none"/>';
// треугольник s += '<line x1="'+A.x+'" y1="'+A.y+'" x2="'+C.x+'" y2="'+C.y+'" stroke="'+COL_ADJ+'" stroke-width="3" stroke-linecap="round"/>';
s += '<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+C.x+','+C.y+'" fill="rgba(217,119,6,.08)" stroke="#b45309" stroke-width="2.2" stroke-linejoin="round"/>'; s += '<line x1="'+C.x+'" y1="'+C.y+'" x2="'+B.x+'" y2="'+B.y+'" stroke="'+COL_OPP+'" stroke-width="3" stroke-linecap="round"/>';
// маркер прямого угла в B (внутренние векторы — uBA и uBC) s += '<line x1="'+A.x+'" y1="'+A.y+'" x2="'+B.x+'" y2="'+B.y+'" stroke="'+COL_HYP+'" stroke-width="3" stroke-linecap="round"/>';
s += '<polyline points="'+rightAngleMark(B, uBA, uBC, 12)+'" fill="none" stroke="#0f172a" stroke-width="1.8"/>'; // маркер прямого угла в C
// дуга угла α при C (от CA к CB) s += '<polyline points="'+rightAngleMark(C, uCA, uCB, 14)+'" fill="none" stroke="#0f172a" stroke-width="1.8"/>';
s += '<path d="'+angleArcAuto(C, uCA, uCB, 30)+'" fill="none" stroke="#dc2626" stroke-width="2"/>'; // дуга угла α при A
s += '<path d="'+angleArcAuto(A, uAC, uAB, 34)+'" fill="none" stroke="#dc2626" stroke-width="2.2"/>';
// подпись α // подпись α
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))}; const midDir = {x:(uAC.x+uAB.x)/2, y:(uAC.y+uAB.y)/2};
s += '<text x="'+aMid.x+'" y="'+aMid.y+'" text-anchor="middle" dominant-baseline="middle" font-family="Inter,sans-serif" font-size="15" font-weight="700" fill="#dc2626">α</text>'; 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 += '<text x="'+aLab.x+'" y="'+aLab.y+'" text-anchor="middle" dominant-baseline="middle" font-family="Inter,sans-serif" font-size="16" font-weight="800" fill="#dc2626">α</text>';
// вершины // вершины
s += '<circle cx="'+A.x+'" cy="'+A.y+'" r="4" fill="#0f172a"/>'; [A,B,C].forEach(P=>{ s+='<circle cx="'+P.x+'" cy="'+P.y+'" r="4" fill="#0f172a"/>'; });
s += '<circle cx="'+B.x+'" cy="'+B.y+'" r="4" fill="#0f172a"/>'; s += '<text x="'+(A.x-14)+'" y="'+(A.y+6)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="16" font-weight="800" fill="#0f172a">A</text>';
s += '<circle cx="'+C.x+'" cy="'+C.y+'" r="4" fill="#0f172a"/>'; s += '<text x="'+(B.x+14)+'" y="'+(B.y+4)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="16" font-weight="800" fill="#0f172a">B</text>';
s += '<text x="'+(A.x-12)+'" y="'+(A.y+18)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="16" font-weight="700" fill="#0f172a">A</text>'; s += '<text x="'+(C.x+14)+'" y="'+(C.y+18)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="16" font-weight="800" fill="#0f172a">C</text>';
s += '<text x="'+(B.x-12)+'" y="'+(B.y-4)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="16" font-weight="700" fill="#0f172a">B</text>'; // длина прилежащего катета AC
s += '<text x="'+(C.x+12)+'" y="'+(C.y+18)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="16" font-weight="700" fill="#0f172a">C</text>'; s += '<text x="'+((A.x+C.x)/2)+'" y="'+(A.y+24)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="'+COL_ADJ+'">b = '+bUnits.toFixed(2)+'</text>';
// подписи сторон // длина противолежащего BC
const labBC = 'BC='+(BCpx/22).toFixed(2); s += '<text x="'+(C.x+18)+'" y="'+((B.y+C.y)/2+5)+'" text-anchor="start" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="'+COL_OPP+'">a = '+aUnits.toFixed(2)+'</text>';
const labAB = 'AB='+(ABpx/22).toFixed(2); // длина гипотенузы AB — над линией
const labAC = 'AC='+(c/22).toFixed(2); const midAB = {x:(A.x+B.x)/2, y:(A.y+B.y)/2};
s += '<text x="'+((B.x+C.x)/2)+'" y="'+(cyBase+34)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">'+labBC+'</text>'; const nAB = {x:-(B.y-A.y), y:(B.x-A.x)};
s += '<text x="'+(B.x-32)+'" y="'+((A.y+B.y)/2)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">'+labAB+'</text>'; const nL = Math.sqrt(nAB.x*nAB.x+nAB.y*nAB.y)||1;
// подпись гипотенузы — поднимем над линией AC const labHyp = {x: midAB.x - 22*nAB.x/nL, y: midAB.y - 22*nAB.y/nL};
const midAC = {x:(A.x+C.x)/2, y:(A.y+C.y)/2}; s += '<text x="'+labHyp.x+'" y="'+labHyp.y+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="'+COL_HYP+'">c = '+cUnits+'</text>';
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; s += '<g font-family="Inter,sans-serif" font-size="11" font-weight="600">';
const labP = {x: midAC.x + 16*nAC.x/nL, y: midAC.y + 16*nAC.y/nL}; s += '<rect x="14" y="14" width="14" height="3" fill="'+COL_HYP+'"/><text x="34" y="20" fill="#0f172a">гипотенуза c</text>';
s += '<text x="'+labP.x+'" y="'+labP.y+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">'+labAC+'</text>'; s += '<rect x="14" y="32" width="14" height="3" fill="'+COL_OPP+'"/><text x="34" y="38" fill="#0f172a">противолежащий a</text>';
s += '<rect x="14" y="50" width="14" height="3" fill="'+COL_ADJ+'"/><text x="34" y="56" fill="#0f172a">прилежащий b</text>';
s += '</g>';
svg.innerHTML = s; svg.innerHTML = s;
// числовые значения // числовые значения
const sn = Math.sin(aRad), cs = Math.cos(aRad), tn = Math.tan(aRad), ct = 1/Math.tan(aRad); 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)+'$ &nbsp;·&nbsp; $\\cos '+aDeg+'^\\circ \\approx '+cs.toFixed(3)+'$<br>' out.innerHTML = '$\\sin '+aDeg+'^\\circ = \\dfrac{a}{c} \\approx '+sn.toFixed(3)+'$ &nbsp;·&nbsp; $\\cos '+aDeg+'^\\circ = \\dfrac{b}{c} \\approx '+cs.toFixed(3)+'$<br>'
+ '$\\tan '+aDeg+'^\\circ \\approx '+tn.toFixed(3)+'$ &nbsp;·&nbsp; $\\cot '+aDeg+'^\\circ \\approx '+ct.toFixed(3)+'$'; + '$\\tan '+aDeg+'^\\circ = \\dfrac{a}{b} \\approx '+tn.toFixed(3)+'$ &nbsp;·&nbsp; $\\cot '+aDeg+'^\\circ = \\dfrac{b}{a} \\approx '+ct.toFixed(3)+'$';
renderMath(out); renderMath(out);
seen.add(aDeg); seen.add(aDeg);
if(seen.size >= 6 && !seen.has('done')){ addXp(10,'p1-iv1'); bumpProgress('p1', 15); seen.add('done'); } if(seen.size >= 6 && !seen.has('done')){ addXp(10,'p1-iv1'); bumpProgress('p1', 15); seen.add('done'); }
@@ -1231,8 +1239,8 @@ function buildP3(){
/* IV1 — Три эталонных треугольника */ /* IV1 — Три эталонных треугольника */
html += `<div class="wg" id="p3-iv1"> html += `<div class="wg" id="p3-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Три эталонных треугольника</div></div> <div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Два эталонных треугольника</div></div>
<div class="wg-help">Слева — треугольник $30^\\circ\\text{-}60^\\circ\\text{-}90^\\circ$ (катеты $1$ и $\\sqrt{3}$, гипотенуза $2$). Справа — равнобедренный $45^\\circ\\text{-}45^\\circ\\text{-}90^\\circ$ (катеты $1$, гипотенуза $\\sqrt{2}$).</div> <div class="wg-help">Слева — треугольник $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$.</div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto"> <div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
<svg id="p3-iv1-svg" viewBox="0 0 600 220" style="width:100%;min-width:420px;height:auto;display:block"></svg> <svg id="p3-iv1-svg" viewBox="0 0 600 220" style="width:100%;min-width:420px;height:auto;display:block"></svg>
</div> </div>
@@ -1304,82 +1312,77 @@ function buildP3(){
box.innerHTML = html; box.innerHTML = html;
renderMath(box); renderMath(box);
/* IV1 — рисуем три эталонных треугольника */ /* IV1 — Два эталонных треугольника (стандартное расположение: прямой угол справа-снизу) */
(function(){ (function(){
const svg = document.getElementById('p3-iv1-svg'); const svg = document.getElementById('p3-iv1-svg');
if(!svg) return; if(!svg) return;
let s = ''; // Стандартная схема для обоих треугольников:
// Треугольник 30-60-90: катеты 1 и sqrt(3), гипотенуза 2. Используем масштаб 60 px = 1 ед. // A — нижний левый угол (острый, на нём показываем основной интересующий нас угол).
// Левый: BC = sqrt(3) (горизонт., прилежащий 30°), AB = 1 (вертикальный, противолежащий 30°) // C — нижний правый угол (прямой).
// Вершины: A слева внизу, B слева вверху (прямой угол), C справа внизу. // B — верхний правый угол (второй острый).
// Угол 30° при C, угол 60° при A. // Прилежащий катет = AC (горизонтальный), противолежащий = BC (вертикальный), гипотенуза = AB.
(function(){ function drawTri(opts){
const u = 60; const { Ax, Ay, b, a, angAname, angBname, sideAdjLab, sideOppLab, hypLab, title } = opts;
const Ax = 40, Ay = 180; const A = {x: Ax, y: Ay};
const A = {x: Ax, y: Ay}; const C = {x: Ax + b, y: Ay};
const B = {x: Ax, y: Ay - u}; // катет AB = 1 const B = {x: C.x, y: Ay - a};
const C = {x: Ax + Math.sqrt(3)*u, y: Ay}; // катет BC = sqrt(3) const uCA = unitVec(C, A), uCB = unitVec(C, B);
const uBA = unitVec(B, A); const uAC = unitVec(A, C), uAB = unitVec(A, B);
const uBC = unitVec(B, C); const uBA = unitVec(B, A), uBC = unitVec(B, C);
const uCA = unitVec(C, A); let s = '';
const uCB = unitVec(C, B); // заполнение и контур треугольника
const uAB = unitVec(A, B); s += '<polygon points="'+A.x+','+A.y+' '+C.x+','+C.y+' '+B.x+','+B.y+'" fill="rgba(217,119,6,.08)" stroke="#b45309" stroke-width="2" stroke-linejoin="round"/>';
const uAC = unitVec(A, C); // маркер прямого угла в C
s += '<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+C.x+','+C.y+'" fill="rgba(217,119,6,.08)" stroke="#b45309" stroke-width="2" stroke-linejoin="round"/>'; s += '<polyline points="'+rightAngleMark(C, uCA, uCB, 11)+'" fill="none" stroke="#0f172a" stroke-width="1.6"/>';
s += '<polyline points="'+rightAngleMark(B, uBA, uBC, 10)+'" fill="none" stroke="#0f172a" stroke-width="1.6"/>'; // дуга и подпись угла при A (основной)
s += '<path d="'+angleArcAuto(C, uCA, uCB, 26)+'" fill="none" stroke="#dc2626" stroke-width="2"/>'; s += '<path d="'+angleArcAuto(A, uAC, uAB, 26)+'" fill="none" stroke="#dc2626" stroke-width="2"/>';
s += '<path d="'+angleArcAuto(A, uAB, uAC, 22)+'" fill="none" stroke="#0ea5e9" stroke-width="2"/>'; 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 += '<text x="'+(C.x-30)+'" y="'+(C.y-8)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#dc2626">30°</text>'; s += '<text x="'+(A.x + 40*midA.x/mAlen)+'" y="'+(A.y + 40*midA.y/mAlen)+'" text-anchor="middle" dominant-baseline="middle" font-family="Inter,sans-serif" font-size="13" font-weight="800" fill="#dc2626">'+angAname+'</text>';
s += '<text x="'+(A.x+18)+'" y="'+(A.y-8)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0ea5e9">60°</text>'; // дуга и подпись угла при B
// вершины и подписи s += '<path d="'+angleArcAuto(B, uBA, uBC, 22)+'" fill="none" stroke="#0ea5e9" stroke-width="2"/>';
['A','B','C'].forEach((nm,i)=>{ const P=[A,B,C][i]; s+='<circle cx="'+P.x+'" cy="'+P.y+'" r="3.5" fill="#0f172a"/>'; }); const midB = {x:(uBA.x+uBC.x)/2, y:(uBA.y+uBC.y)/2};
s += '<text x="'+(A.x-10)+'" y="'+(A.y+15)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="14" font-weight="700">A</text>'; const mBlen = Math.sqrt(midB.x*midB.x+midB.y*midB.y)||1;
s += '<text x="'+(B.x-10)+'" y="'+(B.y-4)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="14" font-weight="700">B</text>'; s += '<text x="'+(B.x + 36*midB.x/mBlen)+'" y="'+(B.y + 36*midB.y/mBlen)+'" text-anchor="middle" dominant-baseline="middle" font-family="Inter,sans-serif" font-size="13" font-weight="800" fill="#0ea5e9">'+angBname+'</text>';
s += '<text x="'+(C.x+10)+'" y="'+(C.y+15)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="14" font-weight="700">C</text>'; // вершины
// длины сторон [A,B,C].forEach(P=>{ s+='<circle cx="'+P.x+'" cy="'+P.y+'" r="3.5" fill="#0f172a"/>'; });
s += '<text x="'+(B.x-22)+'" y="'+((A.y+B.y)/2+4)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">1</text>'; s += '<text x="'+(A.x-12)+'" y="'+(A.y+6)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="14" font-weight="800">A</text>';
s += '<text x="'+((B.x+C.x)/2)+'" y="'+(A.y+22)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">√3</text>'; s += '<text x="'+(B.x+12)+'" y="'+(B.y+4)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="14" font-weight="800">B</text>';
const midAC={x:(A.x+C.x)/2, y:(A.y+C.y)/2}; s += '<text x="'+(C.x+12)+'" y="'+(C.y+16)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="14" font-weight="800">C</text>';
const nAC={x:-(C.y-A.y), y:(C.x-A.x)}; // длина прилежащего AC
const nL=Math.sqrt(nAC.x*nAC.x+nAC.y*nAC.y)||1; s += '<text x="'+((A.x+C.x)/2)+'" y="'+(A.y+22)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#b45309">'+sideAdjLab+'</text>';
const labP={x: midAC.x + 16*nAC.x/nL, y: midAC.y + 16*nAC.y/nL}; // длина противолежащего BC
s += '<text x="'+labP.x+'" y="'+labP.y+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">2</text>'; s += '<text x="'+(C.x+8)+'" y="'+((B.y+C.y)/2+5)+'" text-anchor="start" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#b45309">'+sideOppLab+'</text>';
// длина гипотенузы 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 += '<text x="'+labP.x+'" y="'+labP.y+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="13" font-weight="700" fill="#b45309">'+hypLab+'</text>';
// заголовок // заголовок
s += '<text x="'+((A.x+C.x)/2)+'" y="22" text-anchor="middle" font-family="Unbounded,sans-serif" font-size="13" font-weight="800" fill="#92400e">30\xb0-60\xb0-90\xb0</text>'; s += '<text x="'+((A.x+C.x)/2)+'" y="22" text-anchor="middle" font-family="Unbounded,sans-serif" font-size="13" font-weight="800" fill="#92400e">'+title+'</text>';
})(); return s;
// Треугольник 45-45-90: оба катета 1, гипотенуза sqrt(2). Масштаб тот же. }
(function(){ let s = '';
const u = 90; // покрупнее // 30-60-90 (слева): катет AC = √3 (прилежащий 30°), катет BC = 1 (противолежащий 30°), гипотенуза AB = 2.
const Ax = 360, Ay = 180; // Острый угол A = 30°, острый B = 60°.
const A = {x: Ax, y: Ay}; s += drawTri({
const B = {x: Ax, y: Ay - u}; Ax: 30, Ay: 180,
const C = {x: Ax + u, y: Ay}; b: Math.sqrt(3)*70, // прилежащий к 30° (длинный)
const uBA = unitVec(B, A); a: 70, // противолежащий 30° (короткий)
const uBC = unitVec(B, C); angAname: '30°', angBname: '60°',
const uCA = unitVec(C, A); sideAdjLab: '√3', sideOppLab: '1', hypLab: '2',
const uCB = unitVec(C, B); title: '30°–60°–90°'
const uAB = unitVec(A, B); });
const uAC = unitVec(A, C); // 45-45-90 (справа): оба катета = 1, гипотенуза = √2.
s += '<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+C.x+','+C.y+'" fill="rgba(217,119,6,.08)" stroke="#b45309" stroke-width="2" stroke-linejoin="round"/>'; s += drawTri({
s += '<polyline points="'+rightAngleMark(B, uBA, uBC, 10)+'" fill="none" stroke="#0f172a" stroke-width="1.6"/>'; Ax: 380, Ay: 180,
s += '<path d="'+angleArcAuto(C, uCA, uCB, 28)+'" fill="none" stroke="#dc2626" stroke-width="2"/>'; b: 95,
s += '<path d="'+angleArcAuto(A, uAB, uAC, 26)+'" fill="none" stroke="#0ea5e9" stroke-width="2"/>'; a: 95,
s += '<text x="'+(C.x-30)+'" y="'+(C.y-8)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#dc2626">45°</text>'; angAname: '45°', angBname: '45°',
s += '<text x="'+(A.x+22)+'" y="'+(A.y-8)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0ea5e9">45°</text>'; sideAdjLab: '1', sideOppLab: '1', hypLab: '√2',
['A','B','C'].forEach((nm,i)=>{ const P=[A,B,C][i]; s+='<circle cx="'+P.x+'" cy="'+P.y+'" r="3.5" fill="#0f172a"/>'; }); title: '45°–45°–90°'
s += '<text x="'+(A.x-10)+'" y="'+(A.y+15)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="14" font-weight="700">A</text>'; });
s += '<text x="'+(B.x-10)+'" y="'+(B.y-4)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="14" font-weight="700">B</text>';
s += '<text x="'+(C.x+10)+'" y="'+(C.y+15)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="14" font-weight="700">C</text>';
s += '<text x="'+(B.x-22)+'" y="'+((A.y+B.y)/2+4)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">1</text>';
s += '<text x="'+((B.x+C.x)/2)+'" y="'+(A.y+22)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">1</text>';
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 += '<text x="'+labP.x+'" y="'+labP.y+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">√2</text>';
s += '<text x="'+((A.x+C.x)/2)+'" y="22" text-anchor="middle" font-family="Unbounded,sans-serif" font-size="13" font-weight="800" fill="#92400e">45\xb0-45\xb0-90\xb0</text>';
})();
svg.innerHTML = s; svg.innerHTML = s;
addXp(10,'p3-iv1'); bumpProgress('p3', 15); addXp(10,'p3-iv1'); bumpProgress('p3', 15);
})(); })();