feat(trigcircle): развёртка угла на графике + KaTeX-подписи граф-вида
- развёртка: участок кривой [0, α] выделяется ярче (с свечением) — видно, как угол на окружности «разворачивается» в график - подпись текущего угла (π/3 и т.п.) на вертикальном маркере, KaTeX - подписи делений оси X (π/2, π, 3π/2, 2π) — теперь KaTeX-оверлеем - название функции (y = sin x / cos x / tg x / ctg x) — KaTeX-оверлеем - _ovLabel: любая LaTeX-команда (\pi, \sin…) теперь рендерится через KaTeX Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -192,7 +192,8 @@ class TrigCircleSim {
|
||||
rec = this._ovMap[key] = { el, last: null, boxed: null };
|
||||
}
|
||||
if (rec.last !== latex) {
|
||||
const useK = /\\tfrac|\\sqrt|\\left|\\frac/.test(latex) && (typeof window !== 'undefined' && window.katex);
|
||||
// Любая LaTeX-команда (\pi, \tfrac, \sin…) → KaTeX; простой текст/число — быстро текстом.
|
||||
const useK = /\\/.test(latex) && (typeof window !== 'undefined' && window.katex);
|
||||
if (useK) rec.el.innerHTML = window.katex.renderToString(latex, { throwOnError: false, strict: false, displayMode: false });
|
||||
else rec.el.textContent = latex;
|
||||
rec.last = latex;
|
||||
@@ -651,7 +652,6 @@ class TrigCircleSim {
|
||||
|
||||
const fn = this.graphFn;
|
||||
const col = _TC[fn] || _TC.sin;
|
||||
const lbl = fn==='sin'?'y = sin x':fn==='cos'?'y = cos x':fn==='tan'?'y = tg x':'y = ctg x';
|
||||
const evFn = fn==='sin'?Math.sin:fn==='cos'?Math.cos:fn==='tan'?Math.tan:(x=>1/Math.tan(x));
|
||||
const yR = (fn==='tan'||fn==='cot') ? 4 : 1.5;
|
||||
const xMin = -0.25*Math.PI, xMax = 2.25*Math.PI;
|
||||
@@ -698,19 +698,16 @@ class TrigCircleSim {
|
||||
c.fillText('1', gx-5, sy(1)); c.fillText('−1', gx-5, sy(-1));
|
||||
}
|
||||
|
||||
/* x ticks */
|
||||
const ticks = [[0,'0'],[Math.PI/2,'π/2'],[Math.PI,'π'],[3*Math.PI/2,'3π/2'],[2*Math.PI,'2π']];
|
||||
c.font='500 10px Manrope,sans-serif'; c.fillStyle='rgba(255,255,255,0.20)';
|
||||
c.textAlign='center'; c.textBaseline='top';
|
||||
for (const [v,l] of ticks) {
|
||||
/* x ticks — линии на canvas, подписи KaTeX-оверлеем */
|
||||
const ticks = [[0, '0'], [Math.PI/2, '\\tfrac{\\pi}{2}'], [Math.PI, '\\pi'],
|
||||
[3*Math.PI/2, '\\tfrac{3\\pi}{2}'], [2*Math.PI, '2\\pi']];
|
||||
ticks.forEach(([v, lx], i) => {
|
||||
const xx = sx(v);
|
||||
if (xx < gx+6 || xx > gx+gw-6) continue;
|
||||
c.strokeStyle='rgba(255,255,255,0.05)'; c.lineWidth=1;
|
||||
c.setLineDash([3,3]);
|
||||
c.beginPath(); c.moveTo(xx, gy); c.lineTo(xx, gy+gh); c.stroke();
|
||||
c.setLineDash([]);
|
||||
c.fillText(l, xx, gy+gh+6);
|
||||
}
|
||||
if (xx < gx+6 || xx > gx+gw-6) return;
|
||||
c.strokeStyle='rgba(255,255,255,0.05)'; c.lineWidth=1; c.setLineDash([3,3]);
|
||||
c.beginPath(); c.moveTo(xx, gy); c.lineTo(xx, gy+gh); c.stroke(); c.setLineDash([]);
|
||||
this._ovLabel('gtick' + i, lx, xx, gy + gh + 9, 'rgba(255,255,255,0.55)', 't');
|
||||
});
|
||||
|
||||
/* ── ghost curves (other functions, dimmed) ── */
|
||||
c.save();
|
||||
@@ -782,6 +779,21 @@ class TrigCircleSim {
|
||||
}
|
||||
c.stroke();
|
||||
|
||||
/* ── развёртка: ярче выделяем кривую на [0, α] — как угол «разворачивается» в график ── */
|
||||
{
|
||||
const aMax = Math.min(Math.max(this.angle, 0), xMax);
|
||||
c.strokeStyle = col; c.lineWidth = 4.5; c.lineCap = 'round'; c.lineJoin = 'round';
|
||||
c.shadowColor = col; c.shadowBlur = 6;
|
||||
c.beginPath(); let onS = false;
|
||||
for (let x = 0; x <= aMax + 1e-9; x += step) {
|
||||
const yv = evFn(x);
|
||||
if (!isFinite(yv) || Math.abs(yv) > yR * 2) { onS = false; continue; }
|
||||
const spx = sx(x), spy = sy(yv);
|
||||
if (!onS) { c.moveTo(spx, spy); onS = true; } else c.lineTo(spx, spy);
|
||||
}
|
||||
c.stroke(); c.shadowBlur = 0;
|
||||
}
|
||||
|
||||
/* ── current angle marker ── */
|
||||
const curY = evFn(this.angle);
|
||||
if (isFinite(curY) && Math.abs(curY) <= yR*2) {
|
||||
@@ -802,20 +814,19 @@ class TrigCircleSim {
|
||||
c.beginPath(); c.arc(mx, my, 2, 0, Math.PI*2); c.fill();
|
||||
/* value badge (KaTeX overlay) */
|
||||
this._ovLabel('gval', _latexVal(curY), mx + 12, my - 20, col, 'l', true);
|
||||
/* подпись угла на оси X (развёртка: где текущий угол на графике) */
|
||||
this._ovLabel('gangle', _angleLatex(this.angle) || this._radLbl(this.angle),
|
||||
mx, gy + 5, _TC.violet, 't', true);
|
||||
}
|
||||
|
||||
c.restore();
|
||||
|
||||
/* fn name badge */
|
||||
c.font='bold 13px Manrope,sans-serif';
|
||||
const tm2 = c.measureText(lbl);
|
||||
const bw3 = tm2.width+18, bh3 = 26;
|
||||
c.fillStyle='rgba(12,12,22,0.7)';
|
||||
c.beginPath(); c.roundRect(gx+8, gy+8, bw3, bh3, 8); c.fill();
|
||||
c.strokeStyle = _tcRgba(col, 0.25); c.lineWidth = 1;
|
||||
c.beginPath(); c.roundRect(gx+8, gy+8, bw3, bh3, 8); c.stroke();
|
||||
c.fillStyle = col; c.textAlign='left'; c.textBaseline='middle';
|
||||
c.fillText(lbl, gx+17, gy+21);
|
||||
/* fn name badge (KaTeX-оверлей) */
|
||||
const _glblTex = fn === 'sin' ? 'y = \\sin x'
|
||||
: fn === 'cos' ? 'y = \\cos x'
|
||||
: fn === 'tan' ? 'y = \\operatorname{tg} x'
|
||||
: 'y = \\operatorname{ctg} x';
|
||||
this._ovLabel('glabel', _glblTex, gx + 16, gy + 21, col, 'l', true);
|
||||
}
|
||||
|
||||
/* ═══ Snap particles ═══════════════════════════════════════════════ */
|
||||
|
||||
Reference in New Issue
Block a user