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;
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 += '<rect x="0" y="0" width="400" height="320" fill="none"/>';
// треугольник
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"/>';
// маркер прямого угла в B (внутренние векторы — uBA и uBC)
s += '<polyline points="'+rightAngleMark(B, uBA, uBC, 12)+'" fill="none" stroke="#0f172a" stroke-width="1.8"/>';
// дуга угла α при C (от CA к CB)
s += '<path d="'+angleArcAuto(C, uCA, uCB, 30)+'" fill="none" stroke="#dc2626" stroke-width="2"/>';
// Стороны как отдельные линии разных цветов
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 += '<line x1="'+C.x+'" y1="'+C.y+'" x2="'+B.x+'" y2="'+B.y+'" stroke="'+COL_OPP+'" stroke-width="3" stroke-linecap="round"/>';
s += '<line x1="'+A.x+'" y1="'+A.y+'" x2="'+B.x+'" y2="'+B.y+'" stroke="'+COL_HYP+'" stroke-width="3" stroke-linecap="round"/>';
// маркер прямого угла в C
s += '<polyline points="'+rightAngleMark(C, uCA, uCB, 14)+'" fill="none" stroke="#0f172a" stroke-width="1.8"/>';
// дуга угла α при 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))};
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 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 += '<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"/>';
s += '<circle cx="'+B.x+'" cy="'+B.y+'" r="4" fill="#0f172a"/>';
s += '<circle cx="'+C.x+'" cy="'+C.y+'" r="4" fill="#0f172a"/>';
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="'+(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>';
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>';
// подписи сторон
const labBC = 'BC='+(BCpx/22).toFixed(2);
const labAB = 'AB='+(ABpx/22).toFixed(2);
const labAC = 'AC='+(c/22).toFixed(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>';
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>';
// подпись гипотенузы — поднимем над линией 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 += '<text x="'+labP.x+'" y="'+labP.y+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">'+labAC+'</text>';
[A,B,C].forEach(P=>{ s+='<circle cx="'+P.x+'" cy="'+P.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 += '<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="'+(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>';
// длина прилежащего катета AC
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
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>';
// длина гипотенузы 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 += '<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>';
// легенда
s += '<g font-family="Inter,sans-serif" font-size="11" font-weight="600">';
s += '<rect x="14" y="14" width="14" height="3" fill="'+COL_HYP+'"/><text x="34" y="20" fill="#0f172a">гипотенуза c</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;
// числовые значения
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>'
+ '$\\tan '+aDeg+'^\\circ \\approx '+tn.toFixed(3)+'$ &nbsp;·&nbsp; $\\cot '+aDeg+'^\\circ \\approx '+ct.toFixed(3)+'$';
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 = \\dfrac{a}{b} \\approx '+tn.toFixed(3)+'$ &nbsp;·&nbsp; $\\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 += `<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-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-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$): из него читаются значения для $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">
<svg id="p3-iv1-svg" viewBox="0 0 600 220" style="width:100%;min-width:420px;height:auto;display:block"></svg>
</div>
@@ -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 += '<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(B, uBA, uBC, 10)+'" fill="none" stroke="#0f172a" stroke-width="1.6"/>';
s += '<path d="'+angleArcAuto(C, uCA, uCB, 26)+'" fill="none" stroke="#dc2626" stroke-width="2"/>';
s += '<path d="'+angleArcAuto(A, uAB, uAC, 22)+'" fill="none" stroke="#0ea5e9" stroke-width="2"/>';
// подписи углов
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+18)+'" y="'+(A.y-8)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0ea5e9">60°</text>';
// вершины и подписи
['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"/>'; });
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">√3</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>';
// Стандартная схема для обоих треугольников:
// 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 += '<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"/>';
// маркер прямого угла в C
s += '<polyline points="'+rightAngleMark(C, uCA, uCB, 11)+'" fill="none" stroke="#0f172a" stroke-width="1.6"/>';
// дуга и подпись угла при A (основной)
s += '<path d="'+angleArcAuto(A, uAC, uAB, 26)+'" fill="none" stroke="#dc2626" 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="'+(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>';
// дуга и подпись угла при B
s += '<path d="'+angleArcAuto(B, uBA, uBC, 22)+'" fill="none" stroke="#0ea5e9" stroke-width="2"/>';
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 += '<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>';
// вершины
[A,B,C].forEach(P=>{ s+='<circle cx="'+P.x+'" cy="'+P.y+'" r="3.5" fill="#0f172a"/>'; });
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+12)+'" y="'+(B.y+4)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="14" font-weight="800">B</text>';
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>';
// длина прилежащего AC
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>';
// длина противолежащего BC
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>';
})();
// Треугольник 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 += '<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(B, uBA, uBC, 10)+'" fill="none" stroke="#0f172a" stroke-width="1.6"/>';
s += '<path d="'+angleArcAuto(C, uCA, uCB, 28)+'" fill="none" stroke="#dc2626" stroke-width="2"/>';
s += '<path d="'+angleArcAuto(A, uAB, uAC, 26)+'" fill="none" stroke="#0ea5e9" stroke-width="2"/>';
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>';
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>';
['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"/>'; });
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>';
})();
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;
}
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);
})();