feat(trainer): тема «Окружность» + режим «читать условие с чертежа»
Окружность (новая геом-тема g-circle, 9 кл, π ≈ 3,14 → ответ — конечная десятичная дробь, ученик вводит её): 4 генератора — длина окружности по радиусу (2πr), по диаметру (πd), площадь круга (πr²), длина дуги ((n/360)·2πr, n=45·k, require r·k чётно → дробь конечная). Новые типы фигур: circle (радиус/диаметр/ заливка) и circle-arc (два радиуса под центральным углом + выделенная дуга). Режим «читать значения с чертежа»: у всех 19 геом-генераторов добавлено figurePrompt (краткое условие); переключатель «Текст / На чертеже» (#tr-figmode) на странице, выбор сохраняется в localStorage. В режиме чертежа числа берутся с фигуры, текст минимальный. Движок прокидывает figurePrompt; showStatement выбирает полный текст или промпт; renderFigureToggle показан только для задач с чертежом; для текстовых/алгебраических задач режим скрыт, проверка ответа от режима не зависит. На чертеж n-угольника выведено число сторон (n = …). Верификация: headless-смоук 6968 проверок / 1140 рендеров; ответы окружности конечные и принимаются движком (1600 экземпляров, округление до 2 знаков ok); inline-скрипт парсится; адверсариал-ревью — circle clean, toggle без high/medium (2 low устранены: скрытие тумблера при неудаче генерации + подпись n сторон). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -375,6 +375,7 @@
|
||||
var sp = pts.map(function (pt) { return f.px(pt); });
|
||||
var body = pgon(sp);
|
||||
for (var j = 0; j < sp.length; j++) body += dot(sp[j], 2.3);
|
||||
body += txt(f.px(P(0, 0)), 'n = ' + n, { fill: 'rgba(255,255,255,.92)', size: 12, weight: 700 }); // число сторон — читается с чертежа
|
||||
if (spec.markAngle) {
|
||||
var v = sp[0], prev = sp[(n - 1) % n], next = sp[1];
|
||||
var arc = angleArc(v, prev, next, 16);
|
||||
@@ -418,6 +419,57 @@
|
||||
var between = P((c1.x + c2.x) / 2, Math.min(c1.y, c2.y) - 6);
|
||||
body += txt(between, 'k = ' + fmt(k), { fill: ARC, size: 12.5, weight: 800 });
|
||||
return body;
|
||||
},
|
||||
|
||||
/* Окружность/круг. r ИЛИ d задают размер. show:
|
||||
'radius' — отрезок-радиус, подпись r; 'diameter' — отрезок-диаметр, подпись d;
|
||||
'area' — лёгкая заливка круга + радиус. (Искомая величина — длина/площадь — на
|
||||
чертеже не отмечается, как у площадей: фигура показывает данные.) */
|
||||
'circle': function (spec, p) {
|
||||
var r = num(p, spec.r), d = num(p, spec.d);
|
||||
var radius = (r != null) ? r : (d != null ? d / 2 : null);
|
||||
if (!(radius > 0)) return null;
|
||||
var show = spec.show || 'radius';
|
||||
var f = fit([P(-1, 0), P(1, 0), P(0, -1), P(0, 1)]);
|
||||
var Cs = f.px(P(0, 0)), rad = f.s;
|
||||
var body = '<circle cx="' + r1(Cs.x) + '" cy="' + r1(Cs.y) + '" r="' + r1(rad) +
|
||||
'" fill="' + (show === 'area' ? FILLSH : 'none') + '" stroke="' + STROKE + '" stroke-width="2.6"/>';
|
||||
body += dot(Cs, 2.6);
|
||||
if (show === 'diameter') {
|
||||
var Ld = P(Cs.x - rad, Cs.y), Rd = P(Cs.x + rad, Cs.y);
|
||||
body += ln(Ld, Rd, { dash: true, stroke: DASH, w: 1.8 });
|
||||
body += dot(Ld, 2.2) + dot(Rd, 2.2);
|
||||
body += txt(P(Cs.x, Cs.y - 11), 'd = ' + fmt(d != null ? d : radius * 2), { fill: '#fff', size: 12.5 });
|
||||
} else {
|
||||
var ang = -Math.PI / 4; // радиус в верхне-правый сектор
|
||||
var E = P(Cs.x + Math.cos(ang) * rad, Cs.y + Math.sin(ang) * rad);
|
||||
body += ln(Cs, E, { w: 2 });
|
||||
body += txt(P((Cs.x + E.x) / 2 + 5, (Cs.y + E.y) / 2 - 7), 'r = ' + fmt(radius), { fill: '#fff', size: 12.5, anchor: 'start' });
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
/* Сектор/дуга: окружность (бледная) + два радиуса под центральным углом angle°,
|
||||
дуга выделена; подписаны угол и радиус r. (Длина дуги — искомая, на чертеже нет.) */
|
||||
'circle-arc': function (spec, p) {
|
||||
var r = num(p, spec.r), nAng = num(p, spec.angle);
|
||||
if (!(r > 0) || !(nAng > 0) || nAng >= 360) return null;
|
||||
var f = fit([P(-1, 0), P(1, 0), P(0, -1), P(0, 1)]);
|
||||
var Cs = f.px(P(0, 0)), rad = f.s;
|
||||
function onC(deg) { var a = deg2rad(deg); return P(Cs.x + Math.cos(a) * rad, Cs.y - Math.sin(a) * rad); }
|
||||
var P0 = onC(0), P1 = onC(nAng);
|
||||
var body = '<circle cx="' + r1(Cs.x) + '" cy="' + r1(Cs.y) + '" r="' + r1(rad) +
|
||||
'" fill="none" stroke="rgba(255,255,255,.4)" stroke-width="1.8"/>';
|
||||
var seg = 28, ap = [];
|
||||
for (var i = 0; i <= seg; i++) ap.push(onC(nAng * i / seg));
|
||||
body += '<path d="M ' + ap.map(function (q) { return r1(q.x) + ' ' + r1(q.y); }).join(' L ') +
|
||||
'" fill="none" stroke="' + ARC + '" stroke-width="3.4" stroke-linecap="round"/>';
|
||||
body += ln(Cs, P0, { w: 2 }) + ln(Cs, P1, { w: 2 });
|
||||
body += dot(Cs, 2.6);
|
||||
var amid = deg2rad(nAng / 2);
|
||||
body += txt(P(Cs.x + Math.cos(amid) * rad * 0.36, Cs.y - Math.sin(amid) * rad * 0.36), fmt(nAng) + '°', { fill: '#fff', size: 12 });
|
||||
body += txt(P((Cs.x + P0.x) / 2, (Cs.y + P0.y) / 2 - 9), 'r = ' + fmt(r), { fill: '#fff', size: 12 });
|
||||
return body;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user