feat(trigcircle): Фаза 1 — работа с углами + обзор (тренажёр тригонометрии)
План тренажёра в plans/trig-circle/PLAN.md (всё по теме на окружности, кроме графиков функций). Фаза 1 (аддитивно к рабочему режиму): - Ввод угла в градусах (поле + Enter/кнопка) → goToAngle (нормализует, показывает котерминальность). Подсказка «+360°·k» в бейдже угла. - Тумблер «График/функции» — скрыть график (тема «функции») → круг на всю ширину (переиспользует существующий слой graph + _layout). - Полная сетка табличных углов (16: 0…330°) вместо 8. - Опорный (острый) угол к оси Ox в выводе (основа формул приведения) + знаки sin/cos/tg по текущей четверти. stats() расширен полями refAngle/refDeg. Verified: node --check; headless-смоук (vm + canvas-Proxy) 9/9 — опорный угол 30/150/210→30°, 300→60°, 90→90°, 0→0; знаки по четвертям (II: sin+ cos− tg−; IV: sin− cos+); новые глобальные glue-функции определены. Эмодзи нет (стрелка — inline SVG .ic, tg-неопр. — em-dash). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -130,7 +130,16 @@ class TrigCircleSim {
|
||||
const ct = Math.abs(s) > 1e-9 ? co / s : undefined;
|
||||
const deg = a * 180 / Math.PI;
|
||||
const q = a < Math.PI/2 ? 1 : a < Math.PI ? 2 : a < 3*Math.PI/2 ? 3 : 4;
|
||||
return { angle: a, deg, radLabel: this._radLbl(a), sin: s, cos: co, tan: t, cot: ct, quadrant: q };
|
||||
// Опорный (острый) угол — к ближайшей оси Ox: основа формул приведения.
|
||||
let ref;
|
||||
if (a <= Math.PI / 2) ref = a;
|
||||
else if (a <= Math.PI) ref = Math.PI - a;
|
||||
else if (a <= 3 * Math.PI/2) ref = a - Math.PI;
|
||||
else ref = 2 * Math.PI - a;
|
||||
return {
|
||||
angle: a, deg, radLabel: this._radLbl(a), sin: s, cos: co, tan: t, cot: ct, quadrant: q,
|
||||
refAngle: ref, refDeg: ref * 180 / Math.PI,
|
||||
};
|
||||
}
|
||||
|
||||
/* ═══ Layout ═══════════════════════════════════════════════════════ */
|
||||
@@ -1029,6 +1038,26 @@ if (typeof window !== 'undefined') window.TrigCircleSim = TrigCircleSim;
|
||||
if (window.LabFX) LabFX.sound.play('click');
|
||||
}
|
||||
|
||||
/* Ввод угла в градусах (поле + Enter/кнопка). Принимает любое число (включая <0 и >360),
|
||||
goToAngle нормализует — заодно демонстрирует котерминальность. */
|
||||
function trigSetAngleDeg(inp) {
|
||||
if (!trigSim || !inp) return;
|
||||
const v = parseFloat(String(inp.value || '').replace(',', '.'));
|
||||
if (!isFinite(v)) return;
|
||||
trigSim.goToAngle(v * Math.PI / 180);
|
||||
}
|
||||
function trigAngleKey(e, inp) { if (e && (e.key === 'Enter' || e.keyCode === 13)) trigSetAngleDeg(inp); }
|
||||
|
||||
/* Показать/скрыть график функций (тема «функции» — по умолчанию можно убрать,
|
||||
круг займёт всю ширину). Переиспользует существующий слой 'graph'. */
|
||||
function trigToggleGraph(rowEl) {
|
||||
if (!trigSim) return;
|
||||
const on = rowEl.classList.toggle('active');
|
||||
trigSim.toggleLayer('graph', on);
|
||||
const fns = document.getElementById('trig-graph-fns');
|
||||
if (fns) fns.style.display = on ? '' : 'none';
|
||||
}
|
||||
|
||||
function _trigUpdateUI(s) {
|
||||
const _f = v => {
|
||||
if (v === undefined) return '—';
|
||||
@@ -1050,9 +1079,22 @@ if (typeof window !== 'undefined') window.TrigCircleSim = TrigCircleSim;
|
||||
document.getElementById('trig-v-tan').textContent = _f(s.tan);
|
||||
document.getElementById('trig-v-cot').textContent = _f(s.cot);
|
||||
|
||||
// Angle badge
|
||||
// Angle badge + котерминальные углы (+360°·k)
|
||||
document.getElementById('trig-angle-badge').innerHTML =
|
||||
`${degStr} = ${s.radLabel}<br><span style="font-size:0.72rem;opacity:0.6">${s.angle.toFixed(4)} рад</span>`;
|
||||
`${degStr} = ${s.radLabel}<br><span style="font-size:0.72rem;opacity:0.6">${s.angle.toFixed(4)} рад</span>` +
|
||||
`<br><span style="font-size:0.68rem;opacity:0.5">+ 360°·k (котерминальные)</span>`;
|
||||
|
||||
// Опорный (острый) угол — guarded (панель может не иметь элемента)
|
||||
const refEl = document.getElementById('trig-ref');
|
||||
if (refEl) refEl.textContent = (Math.round(s.refDeg * 10) / 10) + '°';
|
||||
// Знаки функций в текущей четверти
|
||||
const signsEl = document.getElementById('trig-signs');
|
||||
if (signsEl) {
|
||||
const sg = v => (v > 1e-9 ? '+' : v < -1e-9 ? '−' : '0');
|
||||
signsEl.innerHTML =
|
||||
`<b style="color:#EF476F">sin ${sg(s.sin)}</b> · <b style="color:#06D6E0">cos ${sg(s.cos)}</b> · ` +
|
||||
`<b style="color:#FFD166">tg ${s.tan === undefined ? '—' : sg(s.tan)}</b>`;
|
||||
}
|
||||
|
||||
// Stats bar (nice fractions)
|
||||
document.getElementById('trigbar-angle').textContent = degStr;
|
||||
|
||||
@@ -506,6 +506,16 @@
|
||||
<!-- left panel -->
|
||||
<div class="proj-panel" style="width:240px;gap:0">
|
||||
|
||||
<!-- Angle input -->
|
||||
<div class="gp-section-title" style="margin-bottom:8px">Угол, °</div>
|
||||
<div style="display:flex;gap:6px;margin-bottom:14px">
|
||||
<input id="trig-angle-input" type="number" step="1" placeholder="напр. 150"
|
||||
onkeydown="trigAngleKey(event,this)"
|
||||
style="flex:1;min-width:0;padding:7px 10px;border:1.5px solid var(--border-h);border-radius:8px;background:#fff;color:var(--text);font-family:'Manrope',sans-serif;font-size:0.82rem;outline:none" />
|
||||
<button class="preset-btn" style="flex-shrink:0;padding:7px 12px" title="Перейти к углу"
|
||||
onclick="trigSetAngleDeg(document.getElementById('trig-angle-input'))"><svg class="ic" viewBox="0 0 24 24" style="width:14px;height:14px"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg></button>
|
||||
</div>
|
||||
|
||||
<!-- Function toggles -->
|
||||
<div class="gp-section-title" style="margin-bottom:10px">Отрезки</div>
|
||||
<div style="display:flex;flex-direction:column;gap:5px;margin-bottom:14px">
|
||||
@@ -535,9 +545,14 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Graph function selector -->
|
||||
<div class="gp-section-title" style="margin-bottom:8px">График</div>
|
||||
<div style="display:flex;flex-wrap:wrap;gap:5px;margin-bottom:14px">
|
||||
<!-- Graph (functions) — optional, can be hidden to focus on the circle -->
|
||||
<label class="tri-layer-row active" style="margin-bottom:8px" onclick="trigToggleGraph(this)">
|
||||
<span class="tri-dot" style="background:var(--violet);box-shadow:0 0 5px var(--violet)"></span>
|
||||
<span class="tri-layer-name">График</span>
|
||||
<span class="tri-layer-hint" style="color:var(--text-3)">функции</span>
|
||||
<span class="tri-toggle"></span>
|
||||
</label>
|
||||
<div id="trig-graph-fns" style="display:flex;flex-wrap:wrap;gap:5px;margin-bottom:14px">
|
||||
<button class="trig-fn-btn active" onclick="trigSetGraphFn('sin',this)" style="--fc:#EF476F">sin</button>
|
||||
<button class="trig-fn-btn" onclick="trigSetGraphFn('cos',this)" style="--fc:#06D6E0">cos</button>
|
||||
<button class="trig-fn-btn" onclick="trigSetGraphFn('tan',this)" style="--fc:#FFD166">tg</button>
|
||||
@@ -553,6 +568,16 @@
|
||||
<span class="tri-stat-k" style="color:#7BF5A4">ctg</span><span class="tri-stat-v" id="trig-v-cot">—</span>
|
||||
</div>
|
||||
|
||||
<!-- Reference (acute) angle + signs by quadrant -->
|
||||
<div class="gp-section-title" style="margin-bottom:8px">Опорный угол · знаки</div>
|
||||
<div style="display:flex;flex-direction:column;gap:6px;margin-bottom:14px">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;font-size:0.78rem">
|
||||
<span style="color:var(--text-3)">острый угол к оси</span>
|
||||
<span id="trig-ref" style="font-weight:800;color:var(--violet)">—</span>
|
||||
</div>
|
||||
<div id="trig-signs" style="text-align:center;font-size:0.72rem;color:var(--text-2)">—</div>
|
||||
</div>
|
||||
|
||||
<!-- Notable angles -->
|
||||
<div class="gp-section-title" style="margin-bottom:8px">Табличные углы</div>
|
||||
<div style="display:flex;flex-wrap:wrap;gap:4px;margin-bottom:14px">
|
||||
@@ -562,8 +587,16 @@
|
||||
<button class="preset-btn" onclick="trigGoTo(Math.PI/3)">60°</button>
|
||||
<button class="preset-btn" onclick="trigGoTo(Math.PI/2)">90°</button>
|
||||
<button class="preset-btn" onclick="trigGoTo(2*Math.PI/3)">120°</button>
|
||||
<button class="preset-btn" onclick="trigGoTo(3*Math.PI/4)">135°</button>
|
||||
<button class="preset-btn" onclick="trigGoTo(5*Math.PI/6)">150°</button>
|
||||
<button class="preset-btn" onclick="trigGoTo(Math.PI)">180°</button>
|
||||
<button class="preset-btn" onclick="trigGoTo(7*Math.PI/6)">210°</button>
|
||||
<button class="preset-btn" onclick="trigGoTo(5*Math.PI/4)">225°</button>
|
||||
<button class="preset-btn" onclick="trigGoTo(4*Math.PI/3)">240°</button>
|
||||
<button class="preset-btn" onclick="trigGoTo(3*Math.PI/2)">270°</button>
|
||||
<button class="preset-btn" onclick="trigGoTo(5*Math.PI/3)">300°</button>
|
||||
<button class="preset-btn" onclick="trigGoTo(7*Math.PI/4)">315°</button>
|
||||
<button class="preset-btn" onclick="trigGoTo(11*Math.PI/6)">330°</button>
|
||||
</div>
|
||||
|
||||
<!-- Angle info -->
|
||||
|
||||
Reference in New Issue
Block a user