fix(geom7 ch4): переделаны рисунки §21, §22, §25 + добавлены §24

- §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₂.
This commit is contained in:
Maxim Dolgolyov
2026-05-29 09:16:52 +03:00
parent 1c93eb668e
commit 7fbbfad0fe
+149 -55
View File
@@ -769,17 +769,19 @@ function buildP21(){
let svg21=''; let svg21='';
if(G){ if(G){
const b=G.svgBox(280,180,{id:'p21-tri',cell:20}); const b=G.svgBox(300,180,{id:'p21-tri',cell:20});
/* Сделаем треугольник со сторонами разной длины */ /* Скалянный треугольник: AB ≈ 240 (самая длинная), BC ≈ 184, AC ≈ 170 (самая короткая)
const A={x:30,y:150}, B={x:250,y:150}, C={x:90,y:30}; Тогда ∠C — самый большой (напротив AB), ∠B — самый маленький (напротив AC) */
const A={x:30,y:160}, B={x:270,y:160}, C={x:140,y:30};
svg21 = b.open svg21 = b.open
+ G.polygon([A,B,C],{color:'#0891b2',fill:'rgba(8,145,178,.08)'}) + 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(A,B,{color:'#dc2626',width:3,label:'c — самая длин.',labelOffset:18,labelColor:'#dc2626'})
+ G.segment(B,C,{color:'#7c3aed',width:2.5,label:'a',labelOffset:-16}) + G.segment(B,C,{color:'#f59e0b',width:2.5,label:'a',labelOffset:-16,labelColor:'#f59e0b'})
+ G.angle(B,A,C,{color:'#dc2626',r:24,label:'∠B (бол.)',fontSize:11,labelOffset:14}) + G.segment(A,C,{color:'#059669',width:2,label:'b — самая корот.',labelOffset:-16,labelColor:'#059669'})
+ G.angle(A,B,C,{color:'#7c3aed',r:24,label:'∠A',fontSize:11,labelOffset:12}) + G.angle(C,A,B,{color:'#dc2626',r:28,label:'∠C — наиб.',fontSize:11,labelOffset:14})
+ G.angle(C,A,B,{color:'#059669',r:22,label:'∠C',fontSize:11,labelOffset:11}) + 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(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(B.x,B.y,'B',{color:'#1e293b',dx:8,dy:12})
+ G.point(C.x,C.y,'C',{color:'#1e293b',dx:-4,dy:-8}) + G.point(C.x,C.y,'C',{color:'#1e293b',dx:-4,dy:-8})
@@ -789,8 +791,9 @@ function buildP21(){
html += makeCard('rule', 'Прямое свойство', '21.1', ` html += makeCard('rule', 'Прямое свойство', '21.1', `
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Теорема.</b> В треугольнике <b>против бо́льшей стороны лежит бо́льший угол</b>.</p> <p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Теорема.</b> В треугольнике <b>против бо́льшей стороны лежит бо́льший угол</b>.</p>
<div class="svg-host">`+svg21+`</div> <div class="svg-host">`+svg21+`</div>
<p>На рисунке: $AB > AC > BC$ $\\Rightarrow$ $\\angle C > \\angle B > \\angle A$.</p> <p>На рисунке: $AB > BC > AC$ (стороны $c > a > b$) $\\Rightarrow$ $\\angle C > \\angle A > \\angle B$.</p>
<p><b>Правило большого пальца.</b> Самая длинная сторона смотрит на самый большой угол.</p>`); <p style="background:var(--warn-bg);padding:9px 13px;border-radius:8px;border-left:4px solid var(--warn)"><b>Цвет = размер.</b> Красным выделены самая длинная сторона и самый большой угол напротив неё. Зелёным — самая короткая и самый маленький.</p>
<p><b>Правило большого пальца.</b> Самая длинная сторона «смотрит» на самый большой угол.</p>`);
html += makeCard('rule', 'Обратное свойство', '21.2', ` html += makeCard('rule', 'Обратное свойство', '21.2', `
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Теорема (обратная).</b> Против бо́льшего угла лежит бо́льшая сторона.</p> <p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Теорема (обратная).</b> Против бо́льшего угла лежит бо́льшая сторона.</p>
@@ -881,29 +884,50 @@ function buildP22(){
let svgOK='', svgBAD=''; let svgOK='', svgBAD='';
if(G){ if(G){
/* Валидный треугольник со сторонами 5, 6, 7 */ /* === Валидный треугольник 4, 5, 6 ===
const b1=G.svgBox(220,150,{id:'p22-ok',cell:20}); Масштаб: 1 ед = 25 px. AB = 6 ед = 150 px (база)
const A={x:30,y:120}, B={x:180,y:120}, C={x:100,y:40}; Решая систему 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 svgOK = b1.open
+ G.polygon([A,B,C],{color:'#059669',fill:'rgba(16,185,129,.10)'}) + G.polygon([A,B,C],{color:'#059669',width:2.5,fill:'rgba(16,185,129,.12)'})
+ G.segment(A,B,{color:'#059669',width:2,label:'7',labelOffset:14}) + G.segment(A,B,{color:'#065f46',width:2,label:'6',labelOffset:14,labelColor:'#065f46'})
+ G.segment(A,C,{color:'#059669',width:2,label:'5',labelOffset:-14}) + G.segment(A,C,{color:'#065f46',width:2,label:'5',labelOffset:-14,labelColor:'#065f46'})
+ G.segment(B,C,{color:'#059669',width:2,label:'6',labelOffset:-14}) + G.segment(B,C,{color:'#065f46',width:2,label:'4',labelOffset:-14,labelColor:'#065f46'})
+ '<text x="110" y="145" text-anchor="middle" font-size="11" fill="#065f46" font-weight="700">5+6 &gt; 7 ✓</text>' + 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})
+ '<text x="180" y="35" font-size="16" font-weight="900" fill="#10b981">✓</text>'
+ '<text x="110" y="155" text-anchor="middle" font-size="10" fill="#065f46" font-weight="700">4 + 5 &gt; 6 ✓ существует</text>'
+ b1.close; + b1.close;
/* Невалидный «треугольник» 3, 4, 8: AB = 8 длинная, AC=3, BC=4 — не сходится */
const b2=G.svgBox(220,150,{id:'p22-bad',cell:20}); /* === Невалидный: 3, 4, 8 ===
const A2={x:20,y:120}, B2={x:200,y:120}; Масштаб: 1 ед = 25 px. AB = 8*25 = 200 px
/* C "висит" — две дуги не пересекаются */ Дуга от 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 svgBAD = b2.open
+ G.segment(A2,B2,{color:'#dc2626',width:2,label:'8',labelOffset:14}) + G.segment(A2,B2,{color:'#dc2626',width:2.5,label:'8',labelOffset:14,labelColor:'#7f1d1d'})
+ G.arc(A2,30,-Math.PI*0.85,-Math.PI*0.15,{color:'#7c3aed',width:1.5,dash:'3 2'}) /* Дуга от A (радиус 75, верхняя полуокружность) */
+ G.arc(B2,40,Math.PI*1.15,Math.PI*1.85,{color:'#f59e0b',width:1.5,dash:'3 2'}) + '<path d="M '+(A2.x-75)+' '+A2.y+' A 75 75 0 0 1 '+(A2.x+75)+' '+A2.y+'" fill="none" stroke="#7c3aed" stroke-width="2" stroke-dasharray="4 3"/>'
+ '<text x="35" y="70" font-size="10" fill="#7c3aed" font-weight="700">r=3</text>' /* Дуга от B (радиус 100, верхняя полуокружность) */
+ '<text x="160" y="60" font-size="10" fill="#f59e0b" font-weight="700">r=4</text>' + '<path d="M '+(B2.x-100)+' '+B2.y+' A 100 100 0 0 1 '+(B2.x+100)+' '+B2.y+'" fill="none" stroke="#f59e0b" stroke-width="2" stroke-dasharray="4 3"/>'
+ G.point(A2.x,A2.y,'A',{color:'#1e293b',dx:-14,dy:12}) /* Линии-радиусы (показать длины 3 и 4) */
+ G.point(B2.x,B2.y,'B',{color:'#1e293b',dx:8,dy:12}) + G.segment(A2,{x:A2.x,y:A2.y-75},{color:'#7c3aed',width:1.2})
+ '<text x="110" y="145" text-anchor="middle" font-size="11" fill="#7f1d1d" font-weight="700">3+4 &lt; 8 ✗</text>' + G.segment(B2,{x:B2.x,y:B2.y-100},{color:'#f59e0b',width:1.2})
+ '<text x="'+(A2.x+5)+'" y="'+(A2.y-40)+'" font-size="11" font-weight="700" fill="#7c3aed">3</text>'
+ '<text x="'+(B2.x+5)+'" y="'+(B2.y-55)+'" font-size="11" font-weight="700" fill="#f59e0b">4</text>'
/* Двойная стрелка «зазор» */
+ '<line x1="'+(A2.x+75)+'" y1="'+A2.y+'" x2="'+(B2.x-100)+'" y2="'+A2.y+'" stroke="#dc2626" stroke-width="2.5"/>'
+ '<text x="'+((A2.x+75+B2.x-100)/2)+'" y="'+(A2.y-6)+'" text-anchor="middle" font-size="11" font-weight="800" fill="#dc2626">зазор</text>'
+ 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})
+ '<text x="200" y="35" font-size="16" font-weight="900" fill="#dc2626">✗</text>'
+ '<text x="120" y="155" text-anchor="middle" font-size="10" fill="#7f1d1d" font-weight="700">3 + 4 &lt; 8 ✗ не существует</text>'
+ b2.close; + b2.close;
} }
@@ -1090,8 +1114,54 @@ function buildP24(){
const G = window.GEOM7; const G = window.GEOM7;
let html = ''; 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 += '<circle cx="'+(A.x+8)+'" cy="'+(A.y+18)+'" r="2" fill="#f59e0b"/>';
}
return s;
}
let s = b.open + tri(C1,A1,B1) + tri(C2,A2,B2)
+ '<text x="60" y="125" text-anchor="middle" font-size="9" fill="#0891b2" font-weight="700">△ 1</text>'
+ '<text x="170" y="125" text-anchor="middle" font-size="9" fill="#0891b2" font-weight="700">△ 2</text>'
+ '<text x="110" y="65" text-anchor="middle" font-size="20" fill="#10b981" font-weight="900">=</text>'
+ b.close;
return '<div style="text-align:center"><div style="font-size:.78rem;font-weight:700;color:var(--sec-acc-d);margin-bottom:4px">'+title+'</div>'+s+'</div>';
}
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', ` html += makeCard('rule', '4 признака равенства прямоугольных $\\triangle$', '24.1', `
<p>Для прямоугольных треугольников <b>дополнительно к</b> 3 признакам обычных треугольников действуют упрощённые формулировки.</p> <p>Для прямоугольных треугольников <b>дополнительно к</b> 3 признакам обычных треугольников действуют упрощённые формулировки. <b>Цвет = выделенный элемент.</b></p>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;margin:14px 0">`+svgP1+svgP2+svgP3+svgP4+`</div>
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Признак 1.</b> <b>По двум катетам.</b> Если 2 катета одного $\\triangle$ равны 2 катетам другого — треугольники равны. (По 1-му признаку обычных $\\triangle$: 2 стороны + угол $90°$ между ними.)</p> <p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Признак 1.</b> <b>По двум катетам.</b> Если 2 катета одного $\\triangle$ равны 2 катетам другого — треугольники равны. (По 1-му признаку обычных $\\triangle$: 2 стороны + угол $90°$ между ними.)</p>
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Признак 2.</b> <b>По катету и прилежащему острому углу.</b> Если катет и прилежащий к нему острый угол одного $\\triangle$ равны катету и прилежащему углу другого — треугольники равны.</p> <p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Признак 2.</b> <b>По катету и прилежащему острому углу.</b> Если катет и прилежащий к нему острый угол одного $\\triangle$ равны катету и прилежащему углу другого — треугольники равны.</p>
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Признак 3.</b> <b>По гипотенузе и острому углу.</b> Если гипотенуза и острый угол одного $\\triangle$ равны гипотенузе и острому углу другого — треугольники равны.</p> <p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Признак 3.</b> <b>По гипотенузе и острому углу.</b> Если гипотенуза и острый угол одного $\\triangle$ равны гипотенузе и острому углу другого — треугольники равны.</p>
@@ -1188,37 +1258,61 @@ function buildP25(){
let svgBis=''; let svgBis='';
if(G){ if(G){
const b=G.svgBox(280,180,{id:'p25-bis',cell:20}); /* Чистый рисунок: вершина угла O слева, биссектриса вправо вдоль оси x.
const O={x:60,y:140}; Угол ∠AOB = 50° (по 25° от биссектрисы вверх и вниз). */
const A={x:240,y:60}; /* верхняя сторона */ const b=G.svgBox(300,200,{id:'p25-bis',cell:20});
const B={x:260,y:160}; /* нижняя сторона */ const O={x:40,y:100};
/* Биссектриса — между A и B */ const ang=25*Math.PI/180; /* половина угла */
const Bs={x:260,y:108}; 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 на биссектрисе */ /* Точка K на биссектрисе */
const K={x:160,y:110}; const K={x:O.x+150,y:O.y};
/* Перпендикуляры из K к сторонам */ /* F1, F2 — основания перпендикуляров из K на OA и OB */
/* Сторона OA: вектор */
function foot(P, P1, P2){ function foot(P, P1, P2){
const v={x:P2.x-P1.x, y:P2.y-P1.y}; 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 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); 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}; return {x:P1.x+t*v.x, y:P1.y+t*v.y};
} }
const F1=foot(K,O,A); const F1=foot(K,O,Aend);
const F2=foot(K,O,B); const F2=foot(K,O,Bend);
svgBis = b.open svgBis = b.open
+ G.segment(O,A,{color:'#7c3aed',width:2.5}) /* Стороны угла */
+ G.segment(O,B,{color:'#7c3aed',width:2.5}) + G.segment(O,Aend,{color:'#7c3aed',width:2.5})
+ G.segment(O,Bs,{color:'#dc2626',width:2,dash:'5 3'}) + G.segment(O,Bend,{color:'#7c3aed',width:2.5})
+ 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.segment(O,Bs,{color:'#dc2626',width:2,dash:'6 3'})
+ G.rightAngleMark(F1,K,O,{color:'#0891b2',size:8}) /* Дуга, показывающая равенство углов (две одинаковые) */
+ G.rightAngleMark(F2,K,O,{color:'#0891b2',size:8}) + G.arc(O,28,-ang,0,{color:'#dc2626',width:1.5})
+ G.point(O.x,O.y,'O',{color:'#1e293b',dx:-12,dy:14}) + G.arc(O,28,0,ang,{color:'#dc2626',width:1.5})
+ G.point(K.x,K.y,'K',{color:'#dc2626',dx:6,dy:-6,r:3.5}) /* Перпендикуляры от K */
+ G.point(F1.x,F1.y,'',{r:2.5,color:'#0891b2'}) + G.segment(K,F1,{color:'#0891b2',width:2})
+ G.point(F2.x,F2.y,'',{r:2.5,color:'#0891b2'}) + G.segment(K,F2,{color:'#0891b2',width:2})
+ '<text x="80" y="155" font-size="11" font-family="JetBrains Mono,monospace" fill="#475569" font-weight="700">бис.</text>' /* Прямые углы у оснований */
+ 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})
/* Подписи */
+ '<text x="'+(Bs.x-10)+'" y="'+(Bs.y-6)+'" font-size="11" font-family="Unbounded,Inter,sans-serif" font-weight="700" fill="#dc2626">бис.</text>'
+ '<text x="'+(Aend.x-2)+'" y="'+(Aend.y-2)+'" font-size="12" font-family="Unbounded,Inter,sans-serif" font-weight="700" fill="#7c3aed">A</text>'
+ '<text x="'+(Bend.x-2)+'" y="'+(Bend.y+14)+'" font-size="12" font-family="Unbounded,Inter,sans-serif" font-weight="700" fill="#7c3aed">B</text>'
/* Равенство расстояний */
+ '<text x="'+((K.x+F1.x)/2-22)+'" y="'+((K.y+F1.y)/2-2)+'" font-size="11" font-family="JetBrains Mono,monospace" font-weight="700" fill="#0891b2">d</text>'
+ '<text x="'+((K.x+F2.x)/2-22)+'" y="'+((K.y+F2.y)/2+10)+'" font-size="11" font-family="JetBrains Mono,monospace" font-weight="700" fill="#0891b2">d</text>'
+ '<text x="'+(O.x+150)+'" y="195" text-anchor="middle" font-size="11" fill="#065f46" font-weight="700">KF₁ = KF₂ = d</text>'
+ b.close; + b.close;
} }