feat(trainer): красивые решения — KaTeX в пояснениях + π/√ в авто-рендере
Решения/объяснения теперь рендерят формулы внутри пояснений (note), а не только
в строке-формуле: stepHtml пропускает note и запасной tex через renderMixed.
Авто-рендер научился π (πr² → \pi\,r², 2πr → 2·π r) и √ (√(a²+b²) → \sqrt{…}):
_unpretty переводит π→pi с явным умножением к соседям, √→sqrt, ²³→^; _autoProse/
_mathRun включили π в алфавит и в условие «это формула».
Движок: ИСПРАВЛЕН баг exprToLatex — константа/буква перед буквой склеивалась
(«\pir» — KaTeX не знает такой команды); теперь между ними тонкий пробел \,
(«\pi\,r»), цифра перед буквой (2x) не трогается.
Проверка: реплика рендера по ВСЕМ генераторам — 5100 пояснений / 2664 матем.
фрагмента, 0 кириллицы в формулах, проза сохранена (9754/0); смоук v41 99634;
figures 19289; inline парсится.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -226,7 +226,11 @@
|
|||||||
if (_isNeg(node.a)) return '-' + _latex({ k: 'bin', op: '*', a: _negate(node.a), b: node.b }); // -5*x -> «-5x»
|
if (_isNeg(node.a)) return '-' + _latex({ k: 'bin', op: '*', a: _negate(node.a), b: node.b }); // -5*x -> «-5x»
|
||||||
// · между числами и числовыми множителями (2·3, 2·7²); иначе соседство (2x, 3(x+1))
|
// · между числами и числовыми множителями (2·3, 2·7²); иначе соседство (2x, 3(x+1))
|
||||||
var sep = ((node.b.k === 'num' && node.b.v >= 0) || (_isNumFactor(node.a) && _isNumFactor(node.b))) ? ' \\cdot ' : '';
|
var sep = ((node.b.k === 'num' && node.b.v >= 0) || (_isNumFactor(node.a) && _isNumFactor(node.b))) ? ' \\cdot ' : '';
|
||||||
return _mulOperand(node.a) + sep + _mulOperand(node.b);
|
var la = _mulOperand(node.a), lb = _mulOperand(node.b);
|
||||||
|
// буквенная команда (\pi, \tau) или переменная слева, буква справа → тонкий пробел,
|
||||||
|
// иначе склейка даёт «\pir» (KaTeX не знает такой команды). Цифру слева (2x) не трогаем.
|
||||||
|
if (!sep && /[A-Za-z]$/.test(la) && /^[A-Za-z]/.test(lb)) sep = '\\,';
|
||||||
|
return la + sep + lb;
|
||||||
}
|
}
|
||||||
if (op === '%') return _wrapL(node.a, 2) + ' \\bmod ' + _wrapL(node.b, 3);
|
if (op === '%') return _wrapL(node.a, 2) + ' \\bmod ' + _wrapL(node.b, 3);
|
||||||
// + или - (схлопываем a + (-b) -> a - b и a - (-b) -> a + b)
|
// + или - (схлопываем a + (-b) -> a - b и a - (-b) -> a + b)
|
||||||
|
|||||||
+15
-8
@@ -623,15 +623,21 @@
|
|||||||
// (через exprToLatex), остальное экранируется. Так формулы видны и в текст-условиях
|
// (через exprToLatex), остальное экранируется. Так формулы видны и в текст-условиях
|
||||||
// (проценты/дроби/verify/choice), где нет единого latex уравнения.
|
// (проценты/дроби/verify/choice), где нет единого latex уравнения.
|
||||||
// Возврат «косметики» prettyMath к виду, понятному SimExpr-парсеру.
|
// Возврат «косметики» prettyMath к виду, понятному SimExpr-парсеру.
|
||||||
|
// Приведение «красивой» математики к виду, понятному SimExpr-парсеру:
|
||||||
|
// ·×→*, −→-, ÷→/, ²³→^2^3, √→sqrt, π→pi (с явным умножением к соседям: 2πr → 2*pi*r).
|
||||||
function _unpretty(s) {
|
function _unpretty(s) {
|
||||||
return String(s).replace(/[·×]/g, '*').replace(/−/g, '-').replace(/÷/g, '/').replace(/²/g, '^2').replace(/³/g, '^3');
|
s = String(s).replace(/[·×]/g, '*').replace(/÷/g, '/').replace(/−/g, '-')
|
||||||
|
.replace(/²/g, '^2').replace(/³/g, '^3').replace(/√/g, 'sqrt');
|
||||||
|
s = s.replace(/π/g, '@P@');
|
||||||
|
s = s.replace(/([0-9A-Za-z)\]])\s*@P@/g, '$1*@P@').replace(/@P@\s*([0-9A-Za-z(])/g, '@P@*$1');
|
||||||
|
return s.replace(/@P@/g, 'pi');
|
||||||
}
|
}
|
||||||
// Один математический фрагмент → KaTeX-html или null (если не математика / не разобрался).
|
// Один математический фрагмент → KaTeX-html или null (если не математика / не разобрался).
|
||||||
function _mathRun(run) {
|
function _mathRun(run) {
|
||||||
var suffix = '', core = run, t = core.match(/(%|°)$/);
|
var suffix = '', core = run, t = core.match(/(%|°)$/);
|
||||||
if (t) { suffix = (t[1] === '%') ? '\\%' : '^{\\circ}'; core = core.slice(0, -1); }
|
if (t) { suffix = (t[1] === '%') ? '\\%' : '^{\\circ}'; core = core.slice(0, -1); }
|
||||||
// рендерим только если есть оператор/степень/дробь/проценты-градусы (одиночные числа/слова — текстом)
|
// рендерим только если есть оператор/степень/дробь/π/проценты-градусы (одиночные числа/слова — текстом)
|
||||||
var worth = /[\/^=<>+\-−·×÷√]|[0-9][A-Za-z]|²|³/.test(run) || suffix;
|
var worth = /[\/^=<>+\-−·×÷√π]|[0-9][A-Za-z]|²|³/.test(run) || suffix;
|
||||||
if (!worth) return null;
|
if (!worth) return null;
|
||||||
core = _unpretty(core);
|
core = _unpretty(core);
|
||||||
if (!/[0-9A-Za-z]/.test(core)) return null;
|
if (!/[0-9A-Za-z]/.test(core)) return null;
|
||||||
@@ -639,9 +645,9 @@
|
|||||||
if (lx == null) return null;
|
if (lx == null) return null;
|
||||||
return kat(lx + suffix, false);
|
return kat(lx + suffix, false);
|
||||||
}
|
}
|
||||||
// Авто-рендер математики в прозе: латино-цифровые «прогоны» → KaTeX, кириллица/слова — текст.
|
// Авто-рендер математики в прозе: латино-цифровые «прогоны» (вкл. π) → KaTeX, кириллица/слова — текст.
|
||||||
function _autoProse(s) {
|
function _autoProse(s) {
|
||||||
var out = '', re = /[0-9A-Za-z(][\w ().,^*\/+\-=<>≤≥≠·×÷√²³°%]*/g, last = 0, m;
|
var out = '', re = /[0-9A-Za-zπ(][\w ().,^*\/+\-=<>≤≥≠·×÷√²³°%π]*/g, last = 0, m;
|
||||||
while ((m = re.exec(s))) {
|
while ((m = re.exec(s))) {
|
||||||
out += esc(s.slice(last, m.index));
|
out += esc(s.slice(last, m.index));
|
||||||
var run = m[0], tail = '', tm = run.match(/[ ,.;:]+$/);
|
var run = m[0], tail = '', tm = run.match(/[ ,.;:]+$/);
|
||||||
@@ -1057,11 +1063,12 @@
|
|||||||
function stepHtml(st, n) {
|
function stepHtml(st, n) {
|
||||||
if (!st) return '';
|
if (!st) return '';
|
||||||
var num = '<span class="tr-step-n">' + n + '</span>';
|
var num = '<span class="tr-step-n">' + n + '</span>';
|
||||||
var note = st.note ? '<span class="tr-step-note">' + num + esc(st.note) + '</span>'
|
// пояснение: формулы внутри прозы (S = πr², дроби, степени, π·r…) тоже рендерятся в KaTeX
|
||||||
|
var note = st.note ? '<span class="tr-step-note">' + num + renderMixed(st.note) + '</span>'
|
||||||
: '<span class="tr-step-note">' + num + '</span>';
|
: '<span class="tr-step-note">' + num + '</span>';
|
||||||
var math = '';
|
var math = '';
|
||||||
if (st.latex) { var h = kat(st.latex, false); math = '<span class="tr-step-math">' + (h || esc(st.tex || '')) + '</span>'; }
|
if (st.latex) { var h = kat(st.latex, false); math = '<span class="tr-step-math">' + (h || renderMixed(st.tex || '')) + '</span>'; }
|
||||||
else if (st.tex) { math = '<span class="tr-step-math">' + esc(st.tex) + '</span>'; }
|
else if (st.tex) { math = '<span class="tr-step-math">' + renderMixed(st.tex) + '</span>'; }
|
||||||
return '<div class="tr-step">' + note + math + '</div>';
|
return '<div class="tr-step">' + note + math + '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user