feat(math6): симметрия (Гл.6 §4 центральная, §5 осевая) — reflectFold
Math6Anim.reflectFold: на координатной плоскости треугольник плавно переходит на свой образ — центральная (поворот 180° вокруг O, режим 'central') или осевая (отражение через Oy, режим 'axial'); образ показан красным пунктиром, ось/центр выделены. Один компонент закрыл §4 и §5. Headless-safe. Тесты math6: 20/20. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -378,6 +378,43 @@ M.coordGame = function (host, opts) {
|
||||
return { stop: L.stop };
|
||||
};
|
||||
|
||||
/* ============================ ДЕМО 10: СИММЕТРИЯ (осевая / центральная) ============================ */
|
||||
M.reflectFold = function (host, opts) {
|
||||
opts = opts || {}; var mode = opts.mode || 'axial';
|
||||
var W0 = 340, H0 = 340; var sc = sceneCanvas(host, W0, H0); var cap = caption(host, '');
|
||||
var XMIN = -6, XMAX = 6, YMIN = -6, YMAX = 6, pad = 22, period = 4;
|
||||
var fig = [{ x: 1, y: 1 }, { x: 4, y: 1 }, { x: 2, y: 4 }];
|
||||
function img(p) { return mode === 'central' ? { x: -p.x, y: -p.y } : { x: -p.x, y: p.y }; }
|
||||
function X(x) { return pad + (x - XMIN) / (XMAX - XMIN) * (W0 - 2 * pad); }
|
||||
function Y(y) { return H0 - pad - (y - YMIN) / (YMAX - YMIN) * (H0 - 2 * pad); }
|
||||
function poly(ctx, pts, fill, stroke, dash) {
|
||||
ctx.beginPath(); pts.forEach(function (p, i) { var x = X(p.x), y = Y(p.y); if (i) ctx.lineTo(x, y); else ctx.moveTo(x, y); }); ctx.closePath();
|
||||
ctx.setLineDash(dash || []); if (fill) { ctx.fillStyle = fill; ctx.fill(); } ctx.strokeStyle = stroke; ctx.lineWidth = 2; ctx.stroke(); ctx.setLineDash([]);
|
||||
}
|
||||
function draw(t) {
|
||||
var ctx = sc.ctx; if (!ctx) return; var bd = cssVar('--border', '#e2e8f0'), axc = cssVar('--text', '#0f172a');
|
||||
ctx.clearRect(0, 0, W0, H0);
|
||||
ctx.strokeStyle = bd; ctx.lineWidth = 0.7;
|
||||
for (var gx = XMIN; gx <= XMAX; gx++) { ctx.beginPath(); ctx.moveTo(X(gx), Y(YMIN)); ctx.lineTo(X(gx), Y(YMAX)); ctx.stroke(); }
|
||||
for (var gy = YMIN; gy <= YMAX; gy++) { ctx.beginPath(); ctx.moveTo(X(XMIN), Y(gy)); ctx.lineTo(X(XMAX), Y(gy)); ctx.stroke(); }
|
||||
ctx.strokeStyle = axc; ctx.lineWidth = 1.4; ctx.beginPath(); ctx.moveTo(X(XMIN), Y(0)); ctx.lineTo(X(XMAX), Y(0)); ctx.moveTo(X(0), Y(YMIN)); ctx.lineTo(X(0), Y(YMAX)); ctx.stroke();
|
||||
if (mode === 'axial') { ctx.strokeStyle = '#e11d48'; ctx.lineWidth = 2.5; ctx.setLineDash([6, 4]); ctx.beginPath(); ctx.moveTo(X(0), Y(YMIN)); ctx.lineTo(X(0), Y(YMAX)); ctx.stroke(); ctx.setLineDash([]); }
|
||||
else { ctx.fillStyle = '#e11d48'; ctx.beginPath(); ctx.arc(X(0), Y(0), 5, 0, 2 * Math.PI); ctx.fill(); ctx.font = '12px JetBrains Mono, monospace'; ctx.fillText('O', X(0) + 8, Y(0) - 8); }
|
||||
var imgPts = fig.map(img);
|
||||
poly(ctx, imgPts, 'rgba(225,29,72,0.06)', 'rgba(225,29,72,0.5)', [5, 4]);
|
||||
poly(ctx, fig, 'rgba(37,99,235,0.12)', '#2563eb', null);
|
||||
var p = (t % period) / period, e = p < 0.5 ? 2 * p * p : 1 - Math.pow(-2 * p + 2, 2) / 2;
|
||||
var ghost = fig.map(function (pt, i) { var ip = imgPts[i]; return { x: pt.x + (ip.x - pt.x) * e, y: pt.y + (ip.y - pt.y) * e }; });
|
||||
poly(ctx, ghost, 'rgba(217,119,6,0.32)', '#d97706', null);
|
||||
}
|
||||
var L = loop(host, draw);
|
||||
cap.innerHTML = mode === 'central'
|
||||
? 'Центральная симметрия: точка $(x;\\,y)$ переходит в $(-x;\\,-y)$ — поворот на $180°$ вокруг центра $O$.'
|
||||
: 'Осевая симметрия: точка $(x;\\,y)$ переходит в $(-x;\\,y)$ — отражение через ось $Oy$, как складывание листа по оси.';
|
||||
if (W.renderMathInElement) try { W.renderMathInElement(cap, { delimiters: [{ left: '$', right: '$', display: false }], throwOnError: false }); } catch (e) {}
|
||||
return { stop: L.stop };
|
||||
};
|
||||
|
||||
/* ============================ КОМПОНЕНТ: ПОШАГОВЫЙ ПЛЕЕР (DOM, не canvas) ============================ */
|
||||
M.stepPlayer = function (host, opts) {
|
||||
opts = opts || {}; var steps = opts.steps || []; if (!steps.length) return { stop: function () {} };
|
||||
|
||||
@@ -405,9 +405,14 @@ function buildP4(){
|
||||
+'<div id="p4-q2" class="qbox"></div>'
|
||||
+'<div style="display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap">$x\'=$ <input type="number" id="p4-x2" class="tinp" style="width:70px;text-align:center"> $y\'=$ <input type="number" id="p4-y2" class="tinp" style="width:70px;text-align:center"><button class="btn primary" id="p4-go2">Проверить</button></div>'
|
||||
+'<div class="feedback" id="p4-fb2"></div></div>';
|
||||
h+='<div class="wg" id="p4-symfig-wg"><div class="wg-header"><span class="wg-badge">Анимация</span><div class="wg-title">Центральная симметрия вживую</div></div>'
|
||||
+'<div class="wg-help">Жёлтый треугольник плавно поворачивается на $180°$ вокруг центра $O$ и ложится на свой образ (красный пунктир).</div>'
|
||||
+'<div id="p4-symfig"></div></div>';
|
||||
h+=secNav('p3','p5')+readBtn('p4');
|
||||
box.innerHTML=h; renderMath(box);
|
||||
|
||||
(function(){ if(window.Math6Anim) Math6Anim.reflectFold(document.getElementById('p4-symfig'),{mode:'central'}); })();
|
||||
|
||||
(function(){
|
||||
var i2=0,score2=0,cur2=null;
|
||||
function gen2(){ var ax=_pick([-5,-4,-3,-2,-1,1,2,3,4,5]),ay=_pick([-5,-4,-3,-2,-1,1,2,3,4,5]); cur2={ax:ax,ay:ay,rx:-ax,ry:-ay}; }
|
||||
@@ -469,9 +474,14 @@ function buildP5(){
|
||||
+'<div id="p5-q2" class="qbox"></div>'
|
||||
+'<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap"><button class="btn primary" data-ax2="Oy">Ось $Oy$</button><button class="btn primary" data-ax2="Ox">Ось $Ox$</button></div>'
|
||||
+'<div class="feedback" id="p5-fb2"></div></div>';
|
||||
h+='<div class="wg" id="p5-symfig-wg"><div class="wg-header"><span class="wg-badge">Анимация</span><div class="wg-title">Осевая симметрия вживую</div></div>'
|
||||
+'<div class="wg-help">Жёлтый треугольник «складывается» через ось $Oy$ (красный пунктир) и ложится на свой образ.</div>'
|
||||
+'<div id="p5-symfig"></div></div>';
|
||||
h+=secNav('p4','final')+readBtn('p5');
|
||||
box.innerHTML=h; renderMath(box);
|
||||
|
||||
(function(){ if(window.Math6Anim) Math6Anim.reflectFold(document.getElementById('p5-symfig'),{mode:'axial'}); })();
|
||||
|
||||
(function(){
|
||||
var i2=0,score2=0,cur2=null;
|
||||
function gen2(){ var ax=_pick([-4,-3,-2,-1,1,2,3,4]),ay=_pick([-4,-3,-2,-1,1,2,3,4]),axis=_pick(['Oy','Ox']); cur2={ax:ax,ay:ay,axis:axis,rx:axis==='Oy'?-ax:ax,ry:axis==='Oy'?ay:-ay}; }
|
||||
|
||||
Reference in New Issue
Block a user