fix(geom8 ch4): §12 малая дуга вместо большой + §16 слайдер и калькулятор

§12 Card 12.1, 12.3 (угол между касательной и хордой):
- Дуга AB рисовалась с sweep=1 — это ДЛИННАЯ дуга через левую сторону
  (250°). Но теорема говорит про малую дугу 'внутри угла' между
  касательной и хордой, которая на ПРАВОЙ стороне (~110°).
- Изменено на sweep=0 — теперь рисуется правильная малая дуга
  справа, та самая что 'inside the angle'.

§16 Интерактив 1 'PT² = PA·PB':
- Слайдер угла секущей имел range 5..60° но математически возможен
  только до asin(R/PO)=asin(62/147)≈25°. При угле > 25° секущая
  пролетает мимо окружности (disc<0), SVG не рендерится — пользователь
  видел пустой блок.
- Range изменён на 2..22° (с запасом). Default value 12°. Теперь
  всегда рендерится корректный SVG с касательной + секущей.

§16 Интерактив 3 'Калькулятор':
- В результате 'PT = \u221a(PA\u00b7PB)' писались литеральные
  unicode-escape строки (двойные backslash в template literal
  становятся одиночными в строке, но \u221a не trigger escape
  → литеральная строка '\u221a'). Заменено на настоящие
  символы √ и · в коде.
- Добавлен SVG слева от калькулятора с диаграммой PT²=PA·PB
  (касательная PT, секущая PAB из внешней точки P).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-28 20:59:44 +03:00
parent e8bd098427
commit 7cea060179
+39 -14
View File
@@ -4790,8 +4790,8 @@ function buildP12(){
<line x1="150" y1="165" x2="211" y2="78" stroke="#0284c7" stroke-width="2.2"/>
<!-- radius OA dashed -->
<line x1="150" y1="100" x2="150" y2="165" stroke="#64748b" stroke-width="1.4" stroke-dasharray="4,3"/>
<!-- arc AB: A(bottom)→B(upper-right). 110deg arc through right side. CW on screen=sweep=1, large=0 -->
<path d="M 150,165 A 65,65 0 0,1 211,78" fill="none" stroke="#dc2626" stroke-width="3.5"/>
<!-- arc AB: A(bottom)→B(upper-right). Small arc 110° on RIGHT side (inside the angle between tangent and chord). sweep=0, large=0 -->
<path d="M 150,165 A 65,65 0 0,0 211,78" fill="none" stroke="#dc2626" stroke-width="3.5"/>
<!-- angle marker at A between tangent-right and chord AB, radius 18 -->
<!-- tangent right = (1,0): start point (150+18, 165)=(168,165) -->
<!-- chord direction unit: (61,-87)/sqrt(61^2+87^2)=(61,-87)/106.3=(0.574,-0.819) -->
@@ -4840,8 +4840,8 @@ function buildP12(){
<line x1="118" y1="143" x2="170" y2="69" stroke="#0284c7" stroke-width="2.2"/>
<!-- radius OA dashed -->
<line x1="118" y1="88" x2="118" y2="143" stroke="#64748b" stroke-width="1.2" stroke-dasharray="4,3"/>
<!-- arc AB highlighted (from A at 270deg to B at 20deg CW on screen = sweep=1) -->
<path d="M 118,143 A 55,55 0 0,1 170,69" fill="none" stroke="#dc2626" stroke-width="3.5"/>
<!-- arc AB: small 110° arc on RIGHT side (inside angle between tangent and chord). sweep=0 -->
<path d="M 118,143 A 55,55 0 0,0 170,69" fill="none" stroke="#dc2626" stroke-width="3.5"/>
<!-- angle marker at A: tangent right (1,0) to chord direction (52,-74), angle=55deg -->
<!-- chord unit: (52,-74)/90.4=(0.575,-0.819). end=(118+15*0.575, 143+15*(-0.819))=(126.6,130.7) -->
<path d="M 133,143 A 15,15 0 0,0 126.6,130.7" fill="none" stroke="#e11d48" stroke-width="2.2"/>
@@ -6494,8 +6494,8 @@ function buildP16(){
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">PT² = PA · PB (живая демонстрация)</div></div>
<div class="wg-help">Меняй положение секущей слайдером. PT² всегда равно PA·PB.</div>
<div class="sliders">
<label>Угол секущей = <b id="p16-sec-val">25</b>°
<input type="range" min="5" max="60" value="25" id="p16-sec-sl">
<label>Угол секущей = <b id="p16-sec-val">12</b>°
<input type="range" min="2" max="22" value="12" id="p16-sec-sl">
</label>
</div>
<div id="p16-svg-wrap" style="display:flex;justify-content:center"></div>
@@ -6518,14 +6518,39 @@ function buildP16(){
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Калькулятор: PT² = PA · PB</div></div>
<div class="wg-help">Выбери что искать: PT, PA или PB.</div>
<div style="display:flex;gap:8px;margin-bottom:12px;flex-wrap:wrap">
<button class="btn primary" id="p16-mode-pt" style="opacity:1">Найти PT</button>
<button class="btn" id="p16-mode-pa">Найти PA</button>
<button class="btn" id="p16-mode-pb">Найти PB</button>
<div style="display:flex;gap:14px;flex-wrap:wrap;align-items:flex-start;justify-content:center;margin-bottom:10px">
<svg viewBox="0 0 260 170" style="max-width:260px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="105" cy="90" r="55" fill="rgba(124,58,237,.07)" stroke="#7c3aed" stroke-width="2"/>
<circle cx="105" cy="90" r="2.5" fill="#5b21b6"/>
<line x1="105" y1="90" x2="148" y2="55" stroke="#5b21b6" stroke-width="1.3" stroke-dasharray="3,2"/>
<line x1="148" y1="55" x2="240" y2="100" stroke="#dc2626" stroke-width="2.4"/>
<line x1="50" y1="125" x2="240" y2="100" stroke="#0284c7" stroke-width="2.2"/>
<polyline points="143,63 150,66 152,58" fill="none" stroke="#5b21b6" stroke-width="1.6"/>
<circle cx="148" cy="55" r="4" fill="#dc2626"/>
<circle cx="50" cy="125" r="4" fill="#0284c7"/>
<circle cx="160" cy="103" r="4" fill="#0284c7"/>
<circle cx="240" cy="100" r="5" fill="#0891b2"/>
<text x="153" y="50" font-size="11" font-weight="700" fill="#dc2626">T</text>
<text x="40" y="138" font-size="11" font-weight="700" fill="#0284c7">A</text>
<text x="158" y="118" font-size="11" font-weight="700" fill="#0284c7">B</text>
<text x="244" y="105" font-size="11" font-weight="700" fill="#0891b2">P</text>
<text x="100" y="105" font-size="10" font-weight="700" fill="#5b21b6">O</text>
<text x="190" y="74" font-size="11" fill="#dc2626" font-weight="700">PT</text>
<text x="100" y="119" font-size="11" fill="#0284c7" font-weight="700">PA</text>
<text x="195" y="95" font-size="11" fill="#0284c7" font-weight="700">PB</text>
<text x="10" y="22" font-size="12" font-weight="800" fill="#5b21b6">PT² = PA · PB</text>
</svg>
<div style="flex:1;min-width:200px">
<div style="display:flex;gap:8px;margin-bottom:12px;flex-wrap:wrap">
<button class="btn primary" id="p16-mode-pt" style="opacity:1">Найти PT</button>
<button class="btn" id="p16-mode-pa">Найти PA</button>
<button class="btn" id="p16-mode-pb">Найти PB</button>
</div>
<div id="p16-calc-fields" style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px"></div>
<button class="btn primary" id="p16-calc-go">Вычислить</button>
<div id="p16-calc-out" style="display:none;margin-top:10px;padding:10px 14px;border-radius:10px;font-size:.95rem;font-weight:700"></div>
</div>
</div>
<div id="p16-calc-fields" style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px"></div>
<button class="btn primary" id="p16-calc-go">Вычислить</button>
<div id="p16-calc-out" style="display:none;margin-top:10px;padding:10px 14px;border-radius:10px;font-size:.95rem;font-weight:700"></div>
</div>`;
/* ИНТЕРАКТИВ 4 — тренажёр */
@@ -6715,7 +6740,7 @@ function buildP16(){
const out=document.getElementById('p16-calc-out');
const modes={
pt:{label:'Найти PT',fields:[['PA','p16-cpa','4'],['PB','p16-cpb','9']],
calc:()=>{const pa=parseFloat(document.getElementById('p16-cpa').value),pb=parseFloat(document.getElementById('p16-cpb').value);if(isNaN(pa)||isNaN(pb)||pa<=0||pb<=0||pa>=pb)return{err:'PA и PB > 0, PA < PB'};const pt=Math.sqrt(pa*pb);return{ok:'PT = \\u221a(PA\\u00b7PB) = \\u221a('+pa+'\\u00b7'+pb+') = '+pt.toFixed(4)};}},
calc:()=>{const pa=parseFloat(document.getElementById('p16-cpa').value),pb=parseFloat(document.getElementById('p16-cpb').value);if(isNaN(pa)||isNaN(pb)||pa<=0||pb<=0||pa>=pb)return{err:'PA и PB > 0, PA < PB'};const pt=Math.sqrt(pa*pb);return{ok:'PT = (PA·PB) = ('+pa+'·'+pb+') = '+pt.toFixed(4)};}},
pa:{label:'Найти PA',fields:[['PT','p16-cpt2','6'],['PB','p16-cpb2','9']],
calc:()=>{const pt=parseFloat(document.getElementById('p16-cpt2').value),pb=parseFloat(document.getElementById('p16-cpb2').value);if(isNaN(pt)||isNaN(pb)||pt<=0||pb<=0||pt*pt>=pb*pb)return{err:'PT и PB > 0, PT < PB'};const pa=pt*pt/pb;return{ok:'PA = PT²/PB = '+pt+'²/'+pb+' = '+pa.toFixed(4)};}},
pb:{label:'Найти PB',fields:[['PT','p16-cpt3','6'],['PA','p16-cpa3','4']],