fix(geom8 ch4): §3 — подписи T₁/T₂/AT уходят с касательных, §2.1 — убран лишний cx/cy

§3 Интерактив 1 'Две касательные из внешней точки': подписи T₁ и T₂
теперь располагаются по радиальному направлению (наружу от центра)
с большим отступом, не лежат на касательной линии. Подписи длин
AT₁ и AT₂ вынесены ПЕРПЕНДИКУЛЯРНО касательным наружу (через
вычисление нормали к каждой касательной). Все подписи теперь
крупнее (font 13, Unbounded для вершин, JetBrains Mono для длин)
и читаются без наложения на линии.

§3 Интерактив 2 'Доказательство касательных по шагам': те же фиксы
плюс расширен viewBox 260×200 → 280×220 для размещения подписей.

§2 Card 2.1: убран лишний атрибут cx='100' cy='95' на <line> элементе
радиуса (line не имеет атрибутов cx/cy — игнорировалось браузером,
но загромождало код).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-28 18:00:32 +03:00
parent cc7551755b
commit 0bcb9e5f2e
+30 -12
View File
@@ -867,7 +867,7 @@ function buildP2(){
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 200 170" style="max-width:220px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="100" cy="95" r="58" fill="rgba(6,182,212,.08)" stroke="#0891b2" stroke-width="2"/>
<line cx="100" cy="95" x1="100" y1="95" x2="100" y2="37" stroke="#0891b2" stroke-width="2"/>
<line x1="100" y1="95" x2="100" y2="37" stroke="#0891b2" stroke-width="2"/>
<circle cx="100" cy="95" r="3.5" fill="#0e7490"/>
<circle cx="100" cy="37" r="4" fill="#06b6d4"/>
<line x1="30" y1="37" x2="170" y2="37" stroke="#06b6d4" stroke-width="2.5"/>
@@ -1379,6 +1379,20 @@ function buildP3(){
const ur2x=(T2x-cx)/R, ur2y=(T2y-cy)/R;
const ut2x=-(ur2y), ut2y=ur2x;
const rm2=`${(T2x+ur2x*s).toFixed(1)},${(T2y+ur2y*s).toFixed(1)} ${(T2x+ur2x*s+ut2x*s).toFixed(1)},${(T2y+ur2y*s+ut2y*s).toFixed(1)} ${(T2x+ut2x*s).toFixed(1)},${(T2y+ut2y*s).toFixed(1)}`;
/* Position T₁ label OUTSIDE circle, above-left of T₁ (away from upper tangent) */
const T1lx=(T1x-cx)/R, T1ly=(T1y-cy)/R; /* outward unit */
const T1labelX=T1x+T1lx*14-6, T1labelY=T1y+T1ly*14+3;
const T2labelX=T2x+(T2x-cx)/R*14-6, T2labelY=T2y+(T2y-cy)/R*14+10;
/* Length labels OFF the tangent line (perpendicular offset to outer side) */
const m1x=(T1x+Ax)/2, m1y=(T1y+Ay)/2;
const t1dx=Ax-T1x, t1dy=Ay-T1y, t1len=Math.hypot(t1dx,t1dy);
/* Perpendicular outward (upper) */
const n1x=-t1dy/t1len, n1y=t1dx/t1len; /* CCW perpendicular */
const len1lx=m1x+n1x*-14-10, len1ly=m1y+n1y*-14;
const m2x=(T2x+Ax)/2, m2y=(T2y+Ay)/2;
const t2dx=Ax-T2x, t2dy=Ay-T2y, t2len=Math.hypot(t2dx,t2dy);
const n2x=-t2dy/t2len, n2y=t2dx/t2len;
const len2lx=m2x+n2x*14-10, len2ly=m2y+n2y*14+6;
const svg=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="${cx}" cy="${cy}" r="${R}" fill="rgba(2,132,199,.09)" stroke="#0284c7" stroke-width="2"/>
<circle cx="${cx}" cy="${cy}" r="3.5" fill="#0369a1"/>
@@ -1392,12 +1406,12 @@ function buildP3(){
<circle cx="${Ax}" cy="${Ay}" r="5" fill="#0369a1"/>
<polyline points="${rm1}" fill="none" stroke="#0369a1" stroke-width="1.8"/>
<polyline points="${rm2}" fill="none" stroke="#0369a1" stroke-width="1.8"/>
<text x="${(T1x-14).toFixed(1)}" y="${(T1y-6).toFixed(1)}" font-size="11" font-weight="700" fill="#0369a1">T₁</text>
<text x="${(T2x-14).toFixed(1)}" y="${(T2y+14).toFixed(1)}" font-size="11" font-weight="700" fill="#0369a1">T₂</text>
<text x="${cx-14}" y="${cy+5}" font-size="11" font-weight="700" fill="#0369a1">O</text>
<text x="${Ax+5}" y="${Ay+5}" font-size="11" font-weight="700" fill="#0369a1">A</text>
<text x="${(T1x+Ax)/2+3}" y="${(T1y+Ay)/2-4}" font-size="10" fill="#065f46" font-weight="700">${fmt(AT)}</text>
<text x="${(T2x+Ax)/2+3}" y="${(T2y+Ay)/2+10}" font-size="10" fill="#065f46" font-weight="700">${fmt(AT)}</text>
<text x="${T1labelX.toFixed(1)}" y="${T1labelY.toFixed(1)}" font-size="13" font-weight="800" fill="#0369a1" font-family="Unbounded,sans-serif">T₁</text>
<text x="${T2labelX.toFixed(1)}" y="${T2labelY.toFixed(1)}" font-size="13" font-weight="800" fill="#0369a1" font-family="Unbounded,sans-serif">T₂</text>
<text x="${cx-16}" y="${cy+5}" font-size="13" font-weight="800" fill="#0369a1" font-family="Unbounded,sans-serif">O</text>
<text x="${Ax+8}" y="${Ay+5}" font-size="13" font-weight="800" fill="#0369a1" font-family="Unbounded,sans-serif">A</text>
<text x="${len1lx.toFixed(1)}" y="${len1ly.toFixed(1)}" font-size="11" fill="#065f46" font-weight="800" font-family="JetBrains Mono,monospace">AT₁=${fmt(AT)}</text>
<text x="${len2lx.toFixed(1)}" y="${len2ly.toFixed(1)}" font-size="11" fill="#065f46" font-weight="800" font-family="JetBrains Mono,monospace">AT₂=${fmt(AT)}</text>
</svg>`;
svgWrap.innerHTML=svg;
info.textContent='AT₁ = AT₂ = '+fmt(AT)+' (при R = '+R+', |OA| = '+OA+')';
@@ -1451,7 +1465,11 @@ function buildP3(){
equal+=`<text x="${(T2x+Ax)/2+2}" y="${(T2y+Ay)/2+8}" font-size="9" fill="#065f46" font-weight="800">✓</text>`;
}
const mainColor=ph==='done'?'#10b981':'#0284c7';
const svg=`<svg viewBox="0 0 260 200" style="max-width:280px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">
const T1lx=(T1x-cx)/R, T1ly=(T1y-cy)/R;
const T1labelX=T1x+T1lx*16-7, T1labelY=T1y+T1ly*16+4;
const T2lx=(T2x-cx)/R, T2ly=(T2y-cy)/R;
const T2labelX=T2x+T2lx*16-7, T2labelY=T2y+T2ly*16+10;
const svg=`<svg viewBox="0 0 280 220" style="max-width:300px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="${cx}" cy="${cy}" r="${R}" fill="rgba(2,132,199,.08)" stroke="${mainColor}" stroke-width="2"/>
${tri1}${tri2}
<line x1="${cx}" y1="${cy}" x2="${T1x.toFixed(1)}" y2="${T1y.toFixed(1)}" stroke="#0284c7" stroke-width="1.8" stroke-dasharray="4,3"/>
@@ -1464,10 +1482,10 @@ function buildP3(){
<circle cx="${T2x.toFixed(1)}" cy="${T2y.toFixed(1)}" r="4" fill="#0284c7"/>
<circle cx="${cx}" cy="${cy}" r="3.5" fill="#0369a1"/>
<circle cx="${Ax}" cy="${Ay}" r="4.5" fill="#0369a1"/>
<text x="${cx-12}" y="${cy+5}" font-size="11" font-weight="700" fill="#0369a1">O</text>
<text x="${(T1x-12).toFixed(1)}" y="${(T1y-5).toFixed(1)}" font-size="11" font-weight="700" fill="#0369a1">T₁</text>
<text x="${(T2x-12).toFixed(1)}" y="${(T2y+14).toFixed(1)}" font-size="11" font-weight="700" fill="#0369a1">T₂</text>
<text x="${Ax+4}" y="${Ay+5}" font-size="11" font-weight="700" fill="#0369a1">A</text>
<text x="${cx-16}" y="${cy+5}" font-size="13" font-weight="800" fill="#0369a1" font-family="Unbounded,sans-serif">O</text>
<text x="${T1labelX.toFixed(1)}" y="${T1labelY.toFixed(1)}" font-size="13" font-weight="800" fill="#0369a1" font-family="Unbounded,sans-serif">T₁</text>
<text x="${T2labelX.toFixed(1)}" y="${T2labelY.toFixed(1)}" font-size="13" font-weight="800" fill="#0369a1" font-family="Unbounded,sans-serif">T₂</text>
<text x="${Ax+8}" y="${Ay+5}" font-size="13" font-weight="800" fill="#0369a1" font-family="Unbounded,sans-serif">A</text>
</svg>`;
svgEl.innerHTML=svg;
txtEl.innerHTML=steps.find(s=>s.phase===ph).text;