From e8bd0984272677877a46aa1ef701b3df313110f7 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Thu, 28 May 2026 20:52:50 +0300 Subject: [PATCH] =?UTF-8?q?fix(geom8=20ch4):=20=C2=A712-=C2=A714=20?= =?UTF-8?q?=E2=80=94=20=D0=BA=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B3=D0=B5=D0=BE=D0=BC=D0=B5=D1=82=D1=80=D0=B8?= =?UTF-8?q?=D1=8F=20SVG=20(=D0=BA=D0=B0=D1=81=D0=B0=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D0=B0=D1=8F,=20=D1=85=D0=BE=D1=80=D0=B4=D1=8B,=20?= =?UTF-8?q?=D1=81=D0=B5=D0=BA=D1=83=D1=89=D0=B8=D0=B5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §12 (Угол между касательной и хордой): - Card 12.1, 12.3: полностью переписаны. Касательная — настоящая горизонтальная прямая в точке A на нижнем краю окружности; хорда AB к точке B на верхней дуге; маркер угла α радиуса 18 между направлением касательной и хордой. Подсветка дуги AB только обводкой (stroke), без заливки fan-сектора. - Интерактив 1: добавлен корректный маркер угла, дуга stroke-only. §13 (Угол между двумя хордами): - Card 13.1: переписан. 4 точки A,B,C,D через тригонометрию (тестовые углы 200°/20°/80°/280°). Хорды AB и CD пересекаются в P=(141,96) — настоящее аналитическое пересечение. Дуги AC и BD — тонкими толстыми обводками БЕЗ заливок. - Интерактив 1: подсветки дуг переделаны на stroke-only. §14 (Угол между секущими из внешней точки): - Card 14.1: переписан с корректной геометрией секущих. P=(272,92) снаружи; обе секущие — настоящие прямые через P; все 4 точки пересечения вычислены аналитически (через квадратное уравнение). - Интерактив 1: добавлен хелпер secantPoints(P, O, R, θ) который гарантирует, что точки пересечения лежат на одной прямой с P. Заменены произвольные углы на окружности на правильное построение. Все §12-§14 теперь геометрически точны: касательные действительно касательны, хорды действительно пересекаются в указанной P, секущие действительно прямые через внешнюю точку. Co-Authored-By: Claude Opus 4.7 (1M context) --- frontend/textbooks/geometry_8_ch4.html | 349 +++++++++++++++++-------- 1 file changed, 246 insertions(+), 103 deletions(-) diff --git a/frontend/textbooks/geometry_8_ch4.html b/frontend/textbooks/geometry_8_ch4.html index fee5bb4..0d0b1c4 100644 --- a/frontend/textbooks/geometry_8_ch4.html +++ b/frontend/textbooks/geometry_8_ch4.html @@ -4772,23 +4772,42 @@ function buildP12(){ $$\\angle(l,AB) = \\dfrac{1}{2}\\,\\smile AB$$

Здесь $l$ — касательная к окружности в точке $A$, $AB$ — хорда из точки касания $A$. Дуга $\\smile AB$ — та, которая лежит «внутри» угла.

- - - - - - - - - - - l - A - B - O - α - ⌒AB - α = ½⌒AB + + + + + + + + + + + + + + + + + + + + + + + + + + + + α + + l + A + B + O + + ⌒AB + α = ½⌒AB
`); @@ -4807,19 +4826,32 @@ function buildP12(){

Задача 2. Дуга $\\smile AB = 110°$. Найди угол между касательной и хордой $AB$.

Решение. $\\angle = \\dfrac{1}{2}\\cdot 110° = 55°$.

- - - - - - - - - l - A - B - 110° - 55° + + + + + + + + + + + + + + + + + + + + 55° + + l + A + B + O + 110°
`); @@ -4928,21 +4960,37 @@ function buildP12(){ const T2x=Ax, T2y=Ay+tLen; // arc indicator small const large=arcDeg>180?1:0; + // angle marker at A between vertical tangent (dir: upward = 270deg) and chord AB + // Tangent at A: vertical line; angle is between upward direction (0,-1) and chord AB direction + // Chord direction from A to B: (Bx-Ax, By-Ay) + const chDx=Bx-Ax, chDy=By-Ay; + const chLen=Math.sqrt(chDx*chDx+chDy*chDy); + const chNx=chDx/chLen, chNy=chDy/chLen; + // Tangent direction upward from A: (0,-1). Angle of arc = ang degrees. + // Arc from tangent-upward direction to chord direction. + // In SVG: start point of arc = A + r*(0,-1), end = A + r*(chNx, chNy) + const mR=16; + const mSx=Ax, mSy=Ay-mR; // start: tangent direction up + const mEx=Ax+mR*chNx, mEy=Ay+mR*chNy; // end: chord direction + // sweep: going from tangent-upward to chord direction in the CW direction on screen (since chord goes to the right of vertical) + // chNx > 0 means chord goes right of tangent => rotate CW on screen => sweep=1 + const mSweep=(chNx>0)?1:0; svgWrap.innerHTML=` - - - - + + + + + - - + + A - B + B O l - ${arcDeg}° - ${Math.round(ang)}° + ${Math.round(ang)}° + ⌒AB=${arcDeg}° `; info.textContent='Дуга AB = '+arcDeg+'° → Угол (l, AB) = '+Math.round(ang)+'° (= '+arcDeg+'°/2)'; } @@ -5147,24 +5195,58 @@ function buildP13(){ $$\\angle APС = \\dfrac{1}{2}(\\smile AC + \\smile BD)$$

Хорды $AB$ и $CD$ пересекаются в точке $P$ внутри окружности. Угол $\\angle APC$ (и вертикальный ему $\\angle BPD$) равен полусумме дуг $\\smile AC$ и $\\smile BD$.

- - - - - - - - - - - - - A - B - C - D - P - ½(⌢AC+⌢BD) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + α + + A + B + C + D + P + ⌒AC + ⌒BD + α = ½(⌒AC+⌒BD)
`); @@ -5298,8 +5380,8 @@ function buildP13(){ } svgWrap.innerHTML=` - - + + @@ -5529,24 +5611,52 @@ function buildP14(){ $$\\angle P = \\dfrac{1}{2}(\\smile AB - \\smile CD)$$

$P$ — внешняя точка; секущие пересекают окружность в точках $A$, $B$ и $C$, $D$ соответственно ($PA < PB$, $PC < PD$). Дуга $\\smile AB$ — дальняя (большая), $\\smile CD$ — ближняя (меньшая).

- - - - - - - - - - - - - P - C - A - D - B - ∠P = ½(⌢AB − ⌢CD) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + P + C + A + D + B + ∠P = ½(⌒AB − ⌒CD)
`); @@ -5656,43 +5766,76 @@ function buildP14(){ const slB=document.getElementById('p14-arcB-sl'), slS=document.getElementById('p14-arcS-sl'); const vB=document.getElementById('p14-arcB-val'), vS=document.getElementById('p14-arcS-val'); const svgWrap=document.getElementById('p14-svg-wrap'), info=document.getElementById('p14-info'); - const R=60, cx=125, cy=90, W=300, H=184; + const R=60, cx=118, cy=90, W=300, H=184; const Px=278, Py=90; + // secantPoints: find 2 intersections of line through (Px,Py) at angle theta with circle (cx,cy,R) + // returns [{x,y},{x,y}] sorted by t (near first, far second), or null if no intersection + function secantPoints(theta){ + const dx=Math.cos(theta), dy=Math.sin(theta); + const fx=Px-cx, fy=Py-cy; + const b=2*(fx*dx+fy*dy); + const c=fx*fx+fy*fy-R*R; + const disc=b*b-4*c; + if(disc<0) return null; + const t1=(-b-Math.sqrt(disc))/2; + const t2=(-b+Math.sqrt(disc))/2; + return[{x:Px+t1*dx,y:Py+t1*dy},{x:Px+t2*dx,y:Py+t2*dy}]; + } function draw(){ let arcB=+slB.value, arcS=+slS.value; if(arcS>=arcB-5){arcS=arcB-5;slS.value=arcS;} vB.textContent=arcB; vS.textContent=arcS; const ang=Math.round((arcB-arcS)/2); - // A, B endpoints of far chord: arc AB = arcB, symmetric about horizontal from center - const halfB=arcB/2*Math.PI/180; - const Ax=cx+R*Math.cos(Math.PI+halfB), Ay=cy+R*Math.sin(Math.PI+halfB); - const Bx=cx+R*Math.cos(Math.PI-halfB), By=cy+R*Math.sin(Math.PI-halfB); - // C, D endpoints of near chord: arc CD = arcS, symmetric - const halfS=arcS/2*Math.PI/180; - const Ccx=cx+R*Math.cos(Math.PI+halfS), Ccy=cy+R*Math.sin(Math.PI+halfS); - const Dx=cx+R*Math.cos(Math.PI-halfS), Dy=cy+R*Math.sin(Math.PI-halfS); - const large=arcB>180?1:0; - const largeS=arcS>180?1:0; + // Two secant directions from P toward the circle (pointing left from P) + // We want arc AB = arcB and arc CD = arcS, symmetric about horizontal axis + // Use two angles symmetric about PI (pointing left): +(halfB for upper, -halfB for lower) + const halfBRad=arcB/2*Math.PI/180; + const halfSRad=arcS/2*Math.PI/180; + // Secant 1: upper secant through far point A and near point C + // Direction angle = PI + halfB (upward-left for far A), but we want a single line through P + // that intersects circle at C (near, small arc side) and A (far, large arc side). + // The angle from P to the midpoint of arc AB on the left is ~PI. + // Upper secant points in direction PI - halfBRad (slightly upward from left) + const s1=secantPoints(Math.PI-halfBRad); + const s2=secantPoints(Math.PI+halfBRad); + if(!s1||!s2) return; + // s1[0]=C (near), s1[1]=A (far); s2[0]=D (near), s2[1]=B (far) + const Ccx2=s1[0].x, Ccy2=s1[0].y; + const Ax2=s1[1].x, Ay2=s1[1].y; + const Dx2=s2[0].x, Dy2=s2[0].y; + const Bx2=s2[1].x, By2=s2[1].y; + // arc AB: from A to B going through the left side (away from P), large arc when arcB>180 + const largeAB=arcB>180?1:0; + // arc CD: from C to D, small arc (near P side), arcS always < 180 in slider range + const largeCD=arcS>180?1:0; + // arc AB: A(upper-left) to B(lower-left). Short arc through leftmost point (away from P) = arcB deg. + // In SVG (y-down): going CCW on screen from A(upper-left) passes through leftmost point to B(lower-left). + // sweep=0 (CCW on screen). large=0 when arcB<180. + const sweepAB=0; + // arc CD: C(upper-near-right) to D(lower-near-right). Short arc through rightmost point (toward P) = arcS deg. + // In SVG: going CW on screen from C(upper-near) passes through rightmost to D(lower-near). + // sweep=1 (CW on screen). large=0 when arcS<180. + const sweepCD=1; svgWrap.innerHTML=` - - - - + + + + - - - - + + + + - A - B - C - D + A + B + C + D P - ∠P = ${ang}° - ⌢AB=${arcB}° - ⌢CD=${arcS}° + ∠P = ${ang}° + ⌒AB=${arcB}° + ⌒CD=${arcS}° `; info.textContent='⌢AB = '+arcB+'°, ⌢CD = '+arcS+'° → ∠P = ½('+arcB+'−'+arcS+') = '+ang+'°'; }