fix(geom8 ch4): §3 — маркеры прямого угла НА ВНУТРЕННЕЙ стороне касательной (к O)

Маркер прямого угла в точке касания T должен быть на той стороне,
где геометрически находится угол 90° — внутри треугольника OTA,
т.е. между направлениями T→O и T→A.

Раньше использовалось +u_radius (от центра наружу) → маркер
оказывался ВНЕ круга на дальней от A стороне. Изменено на
-u_radius (внутрь, к центру). Теперь маркер показывает угол
90° между OT и tangent правильно.

Затронуты:
- §3 Card 3.1 (статичная)
- §3 Интерактив 1 (slider OA)
- §3 Интерактив 2 (пошаговое доказательство)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-28 18:50:22 +03:00
parent 1d6f97e636
commit ca6b93fb57
+22 -19
View File
@@ -1259,10 +1259,13 @@ function buildP3(){
<!-- OA bisector -->
<line x1="95" y1="100" x2="220" y2="92" stroke="#64748b" stroke-width="1.5" stroke-dasharray="5,3"/>
<circle cx="220" cy="92" r="4" fill="#0369a1"/>
<!-- right-angle at T₂=(115,53): u_r=(20/50,-47/50)=(0.4,-0.94) u_t=(0.94,0.4) s=8 -->
<polyline points="118.2,45.5 125.7,48.7 122.5,56.2" fill="none" stroke="#0369a1" stroke-width="1.6"/>
<!-- right-angle at T₁=(112,146): u_r=(0.34,0.92) u_t=+CW_perp=(0.92,-0.34) toward A s=8 -->
<polyline points="114.7,153.4 122.1,150.7 119.4,143.3" fill="none" stroke="#0369a1" stroke-width="1.6"/>
<!-- right-angle at T₂=(115,53): arms point INWARD (toward O) and toward A -->
<!-- u_r=(0.4,-0.94) from O to T₂; toO=-u_r=(-0.4,0.94); u_t toward A=(0.938,0.348); s=8 -->
<!-- p1=T₂+8·toO=(111.8,60.5) corner=p1+8·u_t=(119.3,63.3) p2=T₂+8·u_t=(122.5,55.8) -->
<polyline points="111.8,60.5 119.3,63.3 122.5,55.8" fill="none" stroke="#0369a1" stroke-width="1.8"/>
<!-- right-angle at T₁=(112,146): u_r=(0.34,0.92) from O to T₁; toO=(-0.34,-0.92); u_t toward A=(0.895,-0.447); s=8 -->
<!-- p1=T₁+8·toO=(109.3,138.6) corner=p1+8·u_t=(116.5,135.0) p2=T₁+8·u_t=(119.2,142.4) -->
<polyline points="109.3,138.6 116.5,135.0 119.2,142.4" fill="none" stroke="#0369a1" stroke-width="1.8"/>
<!-- labels: O left of center, A right of point -->
<text x="79" y="104" font-size="11" font-weight="700" fill="#0369a1">O</text>
<text x="224" y="96" font-size="11" font-weight="700" fill="#0369a1">A</text>
@@ -1391,16 +1394,15 @@ function buildP3(){
const sinA=R/OA, cosA=AT/OA;
const T1x=cx+R*sinA, T1y=cy-R*cosA;
const T2x=cx+R*sinA, T2y=cy+R*cosA;
// right angle markers — ut must point TOWARD A from each tangent point
const ur1x=(T1x-cx)/R, ur1y=(T1y-cy)/R; // T1 is upper; radius points upper-right
// CCW perp of upper-right radius = lower-right = toward A ✓
const ut1x=-(ur1y), ut1y=ur1x;
// right angle markers — arms must point INWARD (toward O) and toward A
const ur1x=(T1x-cx)/R, ur1y=(T1y-cy)/R; // outward radial unit
const ut1x=-(ur1y), ut1y=ur1x; // CCW perp = toward A for upper T1
const s=8;
const rm1=`${(T1x+ur1x*s).toFixed(1)},${(T1y+ur1y*s).toFixed(1)} ${(T1x+ur1x*s+ut1x*s).toFixed(1)},${(T1y+ur1y*s+ut1y*s).toFixed(1)} ${(T1x+ut1x*s).toFixed(1)},${(T1y+ut1y*s).toFixed(1)}`;
const ur2x=(T2x-cx)/R, ur2y=(T2y-cy)/R; // T2 is lower; radius points lower-right
// CW perp of lower-right radius = upper-right = toward A ✓
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)}`;
// marker: T+s·(-u_r), T+s·(-u_r)+s·u_t, T+s·u_t — uses INWARD direction (-u_r)
const rm1=`${(T1x-ur1x*s).toFixed(1)},${(T1y-ur1y*s).toFixed(1)} ${(T1x-ur1x*s+ut1x*s).toFixed(1)},${(T1y-ur1y*s+ut1y*s).toFixed(1)} ${(T1x+ut1x*s).toFixed(1)},${(T1y+ut1y*s).toFixed(1)}`;
const ur2x=(T2x-cx)/R, ur2y=(T2y-cy)/R;
const ut2x=ur2y, ut2y=-ur2x; // CW perp = toward A for lower T2
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;
@@ -1475,12 +1477,13 @@ function buildP3(){
tri1=`<polygon points="${cx},${cy} ${T1x.toFixed(1)},${T1y.toFixed(1)} ${Ax},${Ay}" fill="rgba(6,182,212,.12)" stroke="#06b6d4" stroke-width="1.3"/>`;
tri2=`<polygon points="${cx},${cy} ${T2x.toFixed(1)},${T2y.toFixed(1)} ${Ax},${Ay}" fill="rgba(2,132,199,.10)" stroke="#0284c7" stroke-width="1.3"/>`;
const s=7;
const ur1x=(T1x-cx)/R, ur1y=(T1y-cy)/R; // T1 upper
const ut1x=-(ur1y), ut1y=ur1x; // CCW perp → toward A
const rm1=`${(T1x+ur1x*s).toFixed(1)},${(T1y+ur1y*s).toFixed(1)} ${(T1x+ur1x*s+ut1x*s).toFixed(1)},${(T1y+ur1y*s+ut1y*s).toFixed(1)} ${(T1x+ut1x*s).toFixed(1)},${(T1y+ut1y*s).toFixed(1)}`;
const ur2x=(T2x-cx)/R, ur2y=(T2y-cy)/R; // T2 lower
const ut2x=ur2y, ut2y=-ur2x; // CW perp → toward A ✓
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)}`;
const ur1x=(T1x-cx)/R, ur1y=(T1y-cy)/R; // outward
const ut1x=-(ur1y), ut1y=ur1x; // CCW perp → toward A for upper T1
// marker INWARD: T+s·(-u_r) and T+s·u_t
const rm1=`${(T1x-ur1x*s).toFixed(1)},${(T1y-ur1y*s).toFixed(1)} ${(T1x-ur1x*s+ut1x*s).toFixed(1)},${(T1y-ur1y*s+ut1y*s).toFixed(1)} ${(T1x+ut1x*s).toFixed(1)},${(T1y+ut1y*s).toFixed(1)}`;
const ur2x=(T2x-cx)/R, ur2y=(T2y-cy)/R;
const ut2x=ur2y, ut2y=-ur2x; // CW perp → toward A for lower T2
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)}`;
equal=`<polyline points="${rm1}" fill="none" stroke="#0369a1" stroke-width="1.8"/><polyline points="${rm2}" fill="none" stroke="#0369a1" stroke-width="1.8"/>`;
}
if(ph==='done'||ph==='equal'){