feat(phys9): 129 canvas-цветов на PHYS9_COLORS — тёмная тема работает (Phase 3)

Mass-replace через node-скрипт (без правки HTML/CSS-частей файла):
- ctx.fillStyle = '#xxx' → ctx.fillStyle = (window.PHYS9_COLORS?...:'#xxx')
- ctx.strokeStyle = '#xxx' → аналогично
- ctx.shadowColor = '#xxx' → аналогично
- drawArrow3(..., '#xxx', ...) → drawArrow3(..., PHYS9_COLORS.x|fallback, ...)

Маппинг по физическим смыслам:
- #94a3b8 → forceNormal (slate-400, пунктир/нормаль)
- #475569 → body (тело)
- #1e293b → axis (координат. оси)
- #ef4444 → plotPrimary (основной график)
- #10b981 → force (сила)
- #3b82f6 → liquid (жидкость)
- #0284c7 → velocity (скорость)
- #ea580c → acceleration (ускорение)
- #7c3aed → forceFriction (трение)
- #2563eb → displacement (перемещение)
- ещё 8 других цветов

Все 129 замен с fallback: если PHYS9_COLORS не загружен (старый кеш),
работает прежний #цвет. Тёмная тема автоматически переключается
благодаря get-проперти в phys9_palette.js.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-30 09:32:40 +03:00
parent 75165d900b
commit ce9f29fcd0
+129 -129
View File
@@ -2930,7 +2930,7 @@ function startAnim1() {
ctx.strokeStyle = dark ? '#334155' : '#e2e8f0'; ctx.lineWidth = 1;
ctx.beginPath(); ctx.moveTo(0, y1); ctx.lineTo(W, y1); ctx.stroke();
var x1 = ((t * 65) % (W + 22)) - 11;
ctx.fillStyle = '#3b82f6';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6');
ctx.beginPath(); ctx.arc(x1, y1 - 11, 9, 0, 2*Math.PI); ctx.fill();
// Row 2: accelerated
var y2 = H * 0.57;
@@ -2940,7 +2940,7 @@ function startAnim1() {
ctx.beginPath(); ctx.moveTo(0, y2); ctx.lineTo(W, y2); ctx.stroke();
var ph2 = (t * 0.55) % 2.2;
var x2 = ph2 * ph2 * W * 0.21;
ctx.fillStyle = '#10b981';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981');
ctx.beginPath(); ctx.arc(Math.min(x2, W+9), y2 - 11, 9, 0, 2*Math.PI); ctx.fill();
// Row 3: decelerated
var y3 = H * 0.91;
@@ -2950,7 +2950,7 @@ function startAnim1() {
ctx.beginPath(); ctx.moveTo(0, y3); ctx.lineTo(W, y3); ctx.stroke();
var ph3 = (t * 0.55) % 2.2;
var x3 = W * (2*ph3 - 0.45*ph3*ph3) * 0.22;
ctx.fillStyle = '#f97316';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316');
ctx.beginPath(); ctx.arc(Math.min(x3, W+9), y3 - 11, 9, 0, 2*Math.PI); ctx.fill();
anim1Id = requestAnimationFrame(frame);
}
@@ -2996,7 +2996,7 @@ function upd2() {
ctx.fillText('Поезд: v₁ = '+vt+' м/с', tx+8, ty+16);
// Passenger dot
var px = tx + tw*0.5;
ctx.fillStyle = '#ef4444';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444');
ctx.beginPath(); ctx.arc(px, ty+th*0.5+2, 11, 0, 2*Math.PI); ctx.fill();
ctx.font = 'bold 9px Inter,sans-serif'; ctx.fillStyle = '#fff';
ctx.textAlign = 'center';
@@ -3004,9 +3004,9 @@ function upd2() {
// Arrow: absolute speed
var ay = ty - 12, scale = (W*0.7) / (vt + 10);
var aLen = (vt + vp) * scale;
ctx.strokeStyle = '#10b981'; ctx.lineWidth = 2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.lineWidth = 2.5;
ctx.beginPath(); ctx.moveTo(tx, ay); ctx.lineTo(tx + aLen, ay); ctx.stroke();
ctx.fillStyle = '#10b981';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981');
ctx.beginPath(); ctx.moveTo(tx+aLen,ay); ctx.lineTo(tx+aLen-8,ay-5); ctx.lineTo(tx+aLen-8,ay+5); ctx.fill();
ctx.font = '11px Inter,sans-serif';
ctx.fillText('v_пас = '+vt+'+'+vp+' = '+(vt+vp)+' м/с', tx, ay - 5);
@@ -3042,14 +3042,14 @@ function upd3() {
var ox = W*0.18, oy = H*0.65, sc = (W*0.55) / 120;
// Vector a (horizontal)
var ax = a * sc, ay = 0;
drawArrow3(ctx, ox, oy, ox+ax, oy+ay, '#3b82f6', 'a='+a, true, dark);
drawArrow3(ctx,ox,oy,ox+ax,oy+ay,(window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'), 'a='+a, true, dark);
// Vector b (at angle)
var bx = b*sc*Math.cos(angR), by = -b*sc*Math.sin(angR);
drawArrow3(ctx, ox, oy, ox+bx, oy+by, '#ef4444', 'b='+b, true, dark);
drawArrow3(ctx,ox,oy,ox+bx,oy+by,(window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'), 'b='+b, true, dark);
// Result vector (parallelogram diagonal)
var rx = ax+bx, ry = ay+by;
var cLen = Math.sqrt(rx*rx+ry*ry)/sc;
drawArrow3(ctx, ox, oy, ox+rx, oy+ry, '#10b981', 'c='+cLen.toFixed(1), false, dark);
drawArrow3(ctx,ox,oy,ox+rx,oy+ry,(window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'), 'c='+cLen.toFixed(1), false, dark);
// Dashed parallelogram lines
ctx.strokeStyle = dark ? '#475569' : '#cbd5e1'; ctx.lineWidth = 1;
ctx.setLineDash([4,3]);
@@ -3101,9 +3101,9 @@ function upd4() {
ctx.fillStyle = dark ? '#94a3b8' : '#475569'; ctx.font = '11px Inter,sans-serif';
ctx.fillText('x', ox+W*0.72-6, oy+14); ctx.fillText('y', ox+4, oy-H*0.7+12);
// Vector
ctx.strokeStyle = '#3b82f6'; ctx.lineWidth = 2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.lineWidth = 2.5;
ctx.beginPath(); ctx.moveTo(ox,oy); ctx.lineTo(ox+vx,oy+vy); ctx.stroke();
ctx.fillStyle = '#3b82f6';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6');
var ang = Math.atan2(vy,vx);
ctx.beginPath(); ctx.moveTo(ox+vx,oy+vy);
ctx.lineTo(ox+vx-9*Math.cos(ang-0.4),oy+vy-9*Math.sin(ang-0.4));
@@ -3115,14 +3115,14 @@ function upd4() {
ctx.beginPath(); ctx.moveTo(ox+vx,oy+vy); ctx.lineTo(ox,oy+vy); ctx.stroke();
ctx.setLineDash([]);
// Projection arrows
ctx.strokeStyle = '#ef4444'; ctx.lineWidth = 2;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.lineWidth = 2;
ctx.beginPath(); ctx.moveTo(ox,oy); ctx.lineTo(ox+vx,oy); ctx.stroke();
ctx.fillStyle = '#ef4444'; ctx.font = 'bold 11px Inter,sans-serif';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.font = 'bold 11px Inter,sans-serif';
var axVal = (am*Math.cos(phiR)).toFixed(1);
ctx.fillText('ax='+axVal, ox+vx/2-10, oy+16);
ctx.strokeStyle = '#f59e0b'; ctx.lineWidth = 2;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.warn:'#f59e0b'); ctx.lineWidth = 2;
ctx.beginPath(); ctx.moveTo(ox,oy); ctx.lineTo(ox,oy+vy); ctx.stroke();
ctx.fillStyle = '#f59e0b';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.warn:'#f59e0b');
var ayVal = (am*Math.sin(phiR)).toFixed(1);
ctx.fillText('ay='+ayVal, ox+6, oy+vy/2);
// Angle arc
@@ -3155,22 +3155,22 @@ function upd5() {
// Start point A
var xa = pad, xb = pad + s1*sc, xc = pad + (s1-s2)*sc;
// Draw detour arc (path above)
ctx.strokeStyle = '#ef4444'; ctx.lineWidth = 2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.lineWidth = 2.5;
ctx.beginPath();
ctx.moveTo(xa,y); ctx.quadraticCurveTo((xa+xb)/2, y-38, xb, y);
ctx.stroke();
ctx.fillStyle = '#ef4444'; ctx.font = '11px Inter,sans-serif';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.font = '11px Inter,sans-serif';
ctx.fillText('Путь s₁='+s1+' м', (xa+xb)/2-24, y-42);
// Return path (dashed below)
ctx.strokeStyle = '#f97316'; ctx.lineWidth = 2; ctx.setLineDash([5,3]);
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.lineWidth = 2; ctx.setLineDash([5,3]);
ctx.beginPath(); ctx.moveTo(xb,y); ctx.lineTo(xc,y); ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle = '#f97316';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316');
ctx.fillText('s₂='+s2, (xb+xc)/2-10, y+18);
// Displacement arrow
ctx.strokeStyle = '#3b82f6'; ctx.lineWidth = 2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.lineWidth = 2.5;
ctx.beginPath(); ctx.moveTo(xa, y+30); ctx.lineTo(xc-4, y+30); ctx.stroke();
ctx.fillStyle = '#3b82f6';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6');
var disp = s1-s2;
ctx.beginPath(); ctx.moveTo(xc,y+30); ctx.lineTo(xc-10,y+25); ctx.lineTo(xc-10,y+35); ctx.fill();
ctx.fillText('Перемещение |Δr| = '+(disp)+' м', xa, y+46);
@@ -3229,7 +3229,7 @@ function upd6() {
ctx.fillStyle = dark ? '#94a3b8' : '#475569'; ctx.font = '11px Inter,sans-serif';
ctx.fillText('t, с', ox+gw-4, oy+14); ctx.fillText('x, м', ox+4, tpad-6);
// Line x(t)
ctx.strokeStyle = '#3b82f6'; ctx.lineWidth = 2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.lineWidth = 2.5;
ctx.beginPath();
for (var tc = 0; tc <= tmax; tc++) {
var xc2 = x0 + v * tc;
@@ -3238,7 +3238,7 @@ function upd6() {
if (tc === 0) ctx.moveTo(gxc, gyc); else ctx.lineTo(gxc, gyc);
}
ctx.stroke();
ctx.fillStyle = '#3b82f6'; ctx.font = 'bold 11px Inter,sans-serif';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.font = 'bold 11px Inter,sans-serif';
ctx.fillText('x = '+x0+(v>=0?'+':'')+v+'·t', ox+gw*0.4, tpad+12);
[['v6v',v+' м/с'],['v6x0',x0+' м']].forEach(function(p){
var el=document.getElementById(p[0]); if(el) el.textContent=p[1];
@@ -3290,7 +3290,7 @@ function upd7() {
ctx.fillStyle = dark ? '#94a3b8' : '#475569'; ctx.font = '11px Inter,sans-serif';
ctx.fillText('t, с', ox+gw-4, oy+14); ctx.fillText('x, м', ox+4, tpad-6);
// Line 1
ctx.strokeStyle = '#3b82f6'; ctx.lineWidth = 2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.lineWidth = 2.5;
ctx.beginPath();
for (var tc = 0; tc <= tmax; tc++) {
var xv = x01 + v1*tc;
@@ -3298,10 +3298,10 @@ function upd7() {
if (tc===0) ctx.moveTo(ox, oy-(x01-xmin)/(xmax-xmin)*gh);
}
ctx.stroke();
ctx.fillStyle = '#3b82f6'; ctx.font = 'bold 10px Inter,sans-serif';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.font = 'bold 10px Inter,sans-serif';
ctx.fillText('Тело 1 (v='+v1+')', ox+4, oy-(x01-xmin)/(xmax-xmin)*gh - 5);
// Line 2
ctx.strokeStyle = '#ef4444'; ctx.lineWidth = 2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.lineWidth = 2.5;
ctx.beginPath();
ctx.moveTo(ox, oy-(x02-xmin)/(xmax-xmin)*gh);
for (var tc2 = 0; tc2 <= tmax; tc2++) {
@@ -3309,7 +3309,7 @@ function upd7() {
ctx.lineTo(ox+tc2*(gw/tmax), oy-(xv2-xmin)/(xmax-xmin)*gh);
}
ctx.stroke();
ctx.fillStyle = '#ef4444';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444');
ctx.fillText('Тело 2 (v='+v2+')', ox+gw*0.55, oy-(x02-xmin)/(xmax-xmin)*gh + 14);
// Meeting point
var dv = v1 + v2, dx = x02 - x01;
@@ -3319,11 +3319,11 @@ function upd7() {
if (tmeet > 0 && tmeet < tmax && xmeet >= xmin && xmeet <= xmax) {
var gxm = ox+tmeet*(gw/tmax);
var gym = oy-(xmeet-xmin)/(xmax-xmin)*gh;
ctx.strokeStyle = '#10b981'; ctx.lineWidth = 1; ctx.setLineDash([4,3]);
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.lineWidth = 1; ctx.setLineDash([4,3]);
ctx.beginPath(); ctx.moveTo(gxm,gym); ctx.lineTo(gxm,oy); ctx.stroke();
ctx.beginPath(); ctx.moveTo(ox,gym); ctx.lineTo(gxm,gym); ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle = '#10b981';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981');
ctx.beginPath(); ctx.arc(gxm,gym,5,0,2*Math.PI); ctx.fill();
ctx.font = '10px Inter,sans-serif';
ctx.fillText('t='+tmeet.toFixed(1)+' с', gxm+3, gym-5);
@@ -3355,14 +3355,14 @@ function upd8() {
var pad = W*0.08, segW = W*0.38, y = H*0.42, h = 22;
// Segment 1
ctx.fillStyle = dark ? '#1e3a5f' : '#dbeafe';
ctx.strokeStyle = '#3b82f6'; ctx.lineWidth = 2;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.lineWidth = 2;
ctx.fillRect(pad, y, segW, h); ctx.strokeRect(pad, y, segW, h);
ctx.fillStyle = dark ? '#93c5fd' : '#1d4ed8'; ctx.font = 'bold 11px Inter,sans-serif';
ctx.textAlign = 'center';
ctx.fillText('v₁ = '+v1+' км/ч', pad+segW/2, y+15);
// Segment 2
ctx.fillStyle = dark ? '#1c2d0f' : '#d1fae5';
ctx.strokeStyle = '#10b981'; ctx.lineWidth = 2;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.lineWidth = 2;
ctx.fillRect(pad+segW, y, segW, h); ctx.strokeRect(pad+segW, y, segW, h);
ctx.fillStyle = dark ? '#6ee7b7' : '#065f46';
ctx.fillText('v₂ = '+v2+' км/ч', pad+segW+segW/2, y+15);
@@ -3435,7 +3435,7 @@ function upd9() {
var scY = riverH;
var bx = W*0.12 + ph*drift*scX;
var by = bank1 + ph*scY;
ctx.fillStyle = '#f59e0b';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.warn:'#f59e0b');
ctx.beginPath(); ctx.arc(bx, by, 8, 0, 2*Math.PI); ctx.fill();
ctx.fillStyle = '#fff'; ctx.font = 'bold 9px Inter,sans-serif';
ctx.textAlign = 'center'; ctx.fillText('', bx, by+4); ctx.textAlign = 'left';
@@ -3487,7 +3487,7 @@ function upd10() {
ctx.fillStyle = dark?'#94a3b8':'#475569'; ctx.font='11px Inter,sans-serif';
ctx.fillText('t,с', ox+gw-4, oy+14); ctx.fillText('v,м/с', ox+4, tpad-6);
// v(t) line
ctx.strokeStyle = '#3b82f6'; ctx.lineWidth=2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.lineWidth=2.5;
ctx.beginPath();
ctx.moveTo(ox, oy-v0/vmax*gh);
for (var tc=0; tc<=tmax; tc++) {
@@ -3496,15 +3496,15 @@ function upd10() {
ctx.lineTo(ox+tc*(gw/tmax), oy-Math.min(vc,vmax)/vmax*gh);
}
ctx.stroke();
ctx.fillStyle='#3b82f6'; ctx.font='bold 11px Inter,sans-serif';
ctx.fillStyle=(window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.font='bold 11px Inter,sans-serif';
ctx.fillText('v = '+v0+(a>=0?'+':'')+a+'·t', ox+gw*0.3, tpad+14);
// a annotation
ctx.strokeStyle='#ef4444'; ctx.lineWidth=1.5; ctx.setLineDash([4,3]);
ctx.strokeStyle=(window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.lineWidth=1.5; ctx.setLineDash([4,3]);
var gx1=ox+(gw/tmax), gx2=ox+2*(gw/tmax);
var vy1=oy-(v0+a)/vmax*gh, vy2=oy-(v0+2*a)/vmax*gh;
ctx.beginPath(); ctx.moveTo(gx1,vy1); ctx.lineTo(gx2,vy1); ctx.lineTo(gx2,vy2); ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle='#ef4444'; ctx.font='10px Inter,sans-serif';
ctx.fillStyle=(window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.font='10px Inter,sans-serif';
ctx.fillText('a='+a, gx2+3, (vy1+vy2)/2+4);
[['v10v0',v0+' м/с'],['v10a',a+' м/с²']].forEach(function(p){ var el=document.getElementById(p[0]); if(el) el.textContent=p[1]; });
var vFinal = v0+a*tmax;
@@ -3559,7 +3559,7 @@ function upd11() {
ctx.lineTo(ox+tc*(gw/tmax), oy-Math.min(vc,vmax)/vmax*gh);
}
ctx.lineTo(ox+gw,oy); ctx.closePath(); ctx.fill();
ctx.strokeStyle='#3b82f6'; ctx.lineWidth=2.5;
ctx.strokeStyle=(window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.lineWidth=2.5;
ctx.beginPath(); ctx.moveTo(ox, oy-Math.min(v0,vmax)/vmax*gh);
for (var tc2=0; tc2<=tmax; tc2++) {
var vc2=v0+a*tc2; if(vc2<0) vc2=0;
@@ -3567,7 +3567,7 @@ function upd11() {
}
ctx.stroke();
// Slope annotation
ctx.fillStyle='#3b82f6'; ctx.font='bold 11px Inter,sans-serif';
ctx.fillStyle=(window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.font='bold 11px Inter,sans-serif';
ctx.fillText('v = '+v0+(a>=0?'+':'')+a+'·t', ox+gw*0.45, tpad+14);
ctx.fillStyle=dark?'#94a3b8':'#64748b'; ctx.font='10px Inter,sans-serif';
ctx.fillText('Площадь = путь s', ox+gw*0.22, oy-gh*0.28);
@@ -3619,7 +3619,7 @@ function upd12() {
ctx.fillStyle=dark?'#94a3b8':'#475569'; ctx.font='11px Inter,sans-serif';
ctx.fillText('t,с',ox+gw-4,oy+14); ctx.fillText('x,м',ox+4,tpad-6);
// x(t) curve (parabola)
ctx.strokeStyle='#10b981'; ctx.lineWidth=2.5;
ctx.strokeStyle=(window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.lineWidth=2.5;
ctx.beginPath();
for (var tc=0; tc<=tmax*20; tc++) {
var t2=tc/(tmax*20)*tmax;
@@ -3629,7 +3629,7 @@ function upd12() {
if (tc===0) ctx.moveTo(gxc,gyc); else ctx.lineTo(gxc,gyc);
}
ctx.stroke();
ctx.fillStyle='#10b981'; ctx.font='bold 11px Inter,sans-serif';
ctx.fillStyle=(window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.font='bold 11px Inter,sans-serif';
ctx.fillText('x = '+v0+'t + '+a+'/2·t²', ox+gw*0.3, tpad+14);
[['v12v0',v0+' м/с'],['v12a',a+' м/с²']].forEach(function(p){ var el=document.getElementById(p[0]); if(el) el.textContent=p[1]; });
var el=document.getElementById('res12');
@@ -3670,14 +3670,14 @@ function upd13() {
ctx.fillStyle = dark ? '#94a3b8' : '#64748b'; ctx.font = '10px Inter,sans-serif';
ctx.fillText('r='+r+' м', cx+3, cy-4);
// Point
ctx.fillStyle = '#3b82f6';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6');
ctx.beginPath(); ctx.arc(px,py,7,0,2*Math.PI); ctx.fill();
// Velocity vector (tangential)
var tangX = -Math.sin(angle)*v*0.5*sc*0.8;
var tangY = Math.cos(angle)*v*0.5*sc*0.8;
ctx.strokeStyle = '#10b981'; ctx.lineWidth = 2;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.lineWidth = 2;
ctx.beginPath(); ctx.moveTo(px,py); ctx.lineTo(px+tangX,py+tangY); ctx.stroke();
ctx.fillStyle = '#10b981';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981');
ctx.beginPath(); ctx.moveTo(px+tangX,py+tangY);
ctx.lineTo(px+tangX-7*Math.cos(Math.atan2(tangY,tangX)-0.4),py+tangY-7*Math.sin(Math.atan2(tangY,tangX)-0.4));
ctx.lineTo(px+tangX-7*Math.cos(Math.atan2(tangY,tangX)+0.4),py+tangY-7*Math.sin(Math.atan2(tangY,tangX)+0.4));
@@ -3741,14 +3741,14 @@ function upd14() {
ctx.fillStyle=dark?'#94a3b8':'#64748b'; ctx.font='10px Inter,sans-serif';
ctx.fillText('r='+r, cx+4, cy-4);
// Point
ctx.fillStyle='#f97316';
ctx.fillStyle=(window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316');
ctx.beginPath(); ctx.arc(px,py,7,0,2*Math.PI); ctx.fill();
// Velocity arrow (tangent) — green
var scV = 0.45*sc;
var tvx = -Math.sin(angle)*vms*scV, tvy = Math.cos(angle)*vms*scV;
ctx.strokeStyle='#10b981'; ctx.lineWidth=2;
ctx.strokeStyle=(window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.lineWidth=2;
ctx.beginPath(); ctx.moveTo(px,py); ctx.lineTo(px+tvx,py+tvy); ctx.stroke();
ctx.fillStyle='#10b981';
ctx.fillStyle=(window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981');
var va=Math.atan2(tvy,tvx);
ctx.beginPath(); ctx.moveTo(px+tvx,py+tvy);
ctx.lineTo(px+tvx-7*Math.cos(va-0.4),py+tvy-7*Math.sin(va-0.4));
@@ -3760,9 +3760,9 @@ function upd14() {
var aLen = Math.min(Math.sqrt(avx*avx+avy*avy), R*0.7);
var aang = Math.atan2(cy-py, cx-px);
var eax = px + Math.cos(aang)*aLen, eay = py + Math.sin(aang)*aLen;
ctx.strokeStyle='#ef4444'; ctx.lineWidth=2.5;
ctx.strokeStyle=(window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.lineWidth=2.5;
ctx.beginPath(); ctx.moveTo(px,py); ctx.lineTo(eax,eay); ctx.stroke();
ctx.fillStyle='#ef4444';
ctx.fillStyle=(window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444');
ctx.beginPath(); ctx.moveTo(eax,eay);
ctx.lineTo(eax-8*Math.cos(aang-0.4),eay-8*Math.sin(aang-0.4));
ctx.lineTo(eax-8*Math.cos(aang+0.4),eay-8*Math.sin(aang+0.4)); ctx.fill();
@@ -3901,7 +3901,7 @@ function drawAngularThrow22(deg, v0) {
var bx = pad + v0 * Math.cos(rad) * tMid * scX;
var by = bH - (v0 * Math.sin(rad) * tMid - 0.5 * g * tMid * tMid) * scY;
ctx.beginPath(); ctx.arc(bx, by, 7, 0, Math.PI * 2);
ctx.fillStyle = '#f97316'; ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.fill();
// Angle arc
ctx.strokeStyle = '#0369a1'; ctx.lineWidth = 1.5;
ctx.beginPath(); ctx.arc(pad, bH, 28, -rad, 0, true); ctx.stroke();
@@ -3912,7 +3912,7 @@ function drawAngularThrow22(deg, v0) {
ctx.fillText('L = ' + L.toFixed(1) + ' м', pad + 5, bH + 18);
ctx.fillText('H = ' + Hmax.toFixed(1) + ' м', bx - 20, by - 10);
// Launch arrow
ctx.strokeStyle = '#10b981'; ctx.lineWidth = 2;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.lineWidth = 2;
var arLen = 40;
ctx.beginPath();
ctx.moveTo(pad, bH);
@@ -3954,7 +3954,7 @@ function drawGravity23(m1, m2, r, F) {
if (x2 > W - 30) x2 = W - 30;
// Body 1
ctx.beginPath(); ctx.arc(x1 + r1, cy, r1, 0, Math.PI * 2);
ctx.fillStyle = '#1d4ed8'; ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.forceGravity:'#1d4ed8'); ctx.fill();
ctx.fillStyle = '#fff'; ctx.font = 'bold 11px Inter'; ctx.textAlign = 'center';
ctx.fillText(m1 + ' кг', x1 + r1, cy + 4);
// Body 2
@@ -3965,10 +3965,10 @@ function drawGravity23(m1, m2, r, F) {
// Arrows
var maxArr = 60, normF = Math.log10(F + 1e-11) + 11;
var arrLen = Math.max(8, Math.min(maxArr, normF * 8));
ctx.strokeStyle = '#f97316'; ctx.lineWidth = 2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.lineWidth = 2.5;
// Left arrow (toward right)
ctx.beginPath(); ctx.moveTo(x1 + r1, cy); ctx.lineTo(x1 + r1 + arrLen, cy); ctx.stroke();
ctx.beginPath(); ctx.moveTo(x1 + r1 + arrLen, cy - 5); ctx.lineTo(x1 + r1 + arrLen + 8, cy); ctx.lineTo(x1 + r1 + arrLen, cy + 5); ctx.fillStyle = '#f97316'; ctx.fill();
ctx.beginPath(); ctx.moveTo(x1 + r1 + arrLen, cy - 5); ctx.lineTo(x1 + r1 + arrLen + 8, cy); ctx.lineTo(x1 + r1 + arrLen, cy + 5); ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.fill();
// Right arrow (toward left)
ctx.beginPath(); ctx.moveTo(x2 - r2, cy); ctx.lineTo(x2 - r2 - arrLen, cy); ctx.stroke();
ctx.beginPath(); ctx.moveTo(x2 - r2 - arrLen, cy - 5); ctx.lineTo(x2 - r2 - arrLen - 8, cy); ctx.lineTo(x2 - r2 - arrLen, cy + 5); ctx.fill();
@@ -3976,7 +3976,7 @@ function drawGravity23(m1, m2, r, F) {
ctx.fillStyle = dark ? '#94a3b8' : '#64748b'; ctx.font = '11px Inter'; ctx.textAlign = 'center';
ctx.fillText('r = ' + r + ' м', (x1 + r1 + x2 - r2) / 2, cy + 30);
// F label
ctx.fillStyle = '#f97316'; ctx.font = 'bold 12px Inter';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.font = 'bold 12px Inter';
ctx.fillText('F = ' + F.toExponential(2) + ' Н', (x1 + r1 + x2 - r2) / 2, cy - 20);
ctx.textAlign = 'left';
}
@@ -4027,31 +4027,31 @@ function drawElevator24(m, a, P, Ft) {
ctx.beginPath(); ctx.moveTo(px - 10, pBase - 10); ctx.lineTo(px + 10, pBase - 10); ctx.stroke();
// Weight arrow (F_t - downward, red)
var g = 10, sc = Math.max(15, Math.min(60, Ft / (m * g) * 35));
ctx.strokeStyle = '#ef4444'; ctx.lineWidth = 2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.lineWidth = 2.5;
ctx.beginPath(); ctx.moveTo(px + 25, pBase - 20); ctx.lineTo(px + 25, pBase - 20 + sc); ctx.stroke();
ctx.beginPath(); ctx.moveTo(px + 22, pBase - 20 + sc - 6); ctx.lineTo(px + 25, pBase - 20 + sc + 4); ctx.lineTo(px + 28, pBase - 20 + sc - 6); ctx.fillStyle = '#ef4444'; ctx.fill();
ctx.fillStyle = '#ef4444'; ctx.font = '10px Inter'; ctx.textAlign = 'left';
ctx.beginPath(); ctx.moveTo(px + 22, pBase - 20 + sc - 6); ctx.lineTo(px + 25, pBase - 20 + sc + 4); ctx.lineTo(px + 28, pBase - 20 + sc - 6); ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.font = '10px Inter'; ctx.textAlign = 'left';
ctx.fillText('F_т=' + Ft.toFixed(0) + 'Н', px + 30, pBase - 20 + sc / 2);
// Reaction (P - upward, green), clamped to 0 if weightless
var Pdisp = Math.max(0, P);
var scP = Math.max(0, Math.min(60, Pdisp / (m * g) * 35));
ctx.strokeStyle = '#10b981'; ctx.lineWidth = 2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.lineWidth = 2.5;
ctx.beginPath(); ctx.moveTo(px - 25, pBase); ctx.lineTo(px - 25, pBase - scP); ctx.stroke();
if (scP > 3) {
ctx.beginPath(); ctx.moveTo(px - 28, pBase - scP + 6); ctx.lineTo(px - 25, pBase - scP - 4); ctx.lineTo(px - 22, pBase - scP + 6); ctx.fillStyle = '#10b981'; ctx.fill();
ctx.beginPath(); ctx.moveTo(px - 28, pBase - scP + 6); ctx.lineTo(px - 25, pBase - scP - 4); ctx.lineTo(px - 22, pBase - scP + 6); ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.fill();
}
ctx.fillStyle = '#10b981'; ctx.font = '10px Inter'; ctx.textAlign = 'right';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.font = '10px Inter'; ctx.textAlign = 'right';
ctx.fillText('P=' + Pdisp.toFixed(0) + 'Н', px - 28, pBase - scP / 2);
// Acceleration arrow
if (a !== 0) {
ctx.strokeStyle = '#f97316'; ctx.lineWidth = 2;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.lineWidth = 2;
var arDir = a > 0 ? -1 : 1;
ctx.beginPath(); ctx.moveTo(ex - 20, ey + eh / 2); ctx.lineTo(ex - 20, ey + eh / 2 + arDir * 35); ctx.stroke();
ctx.beginPath();
var arTip = ey + eh / 2 + arDir * 35;
ctx.moveTo(ex - 23, arTip - arDir * 6); ctx.lineTo(ex - 20, arTip + arDir * 4); ctx.lineTo(ex - 17, arTip - arDir * 6);
ctx.fillStyle = '#f97316'; ctx.fill();
ctx.fillStyle = '#f97316'; ctx.font = 'bold 10px Inter'; ctx.textAlign = 'center';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.font = 'bold 10px Inter'; ctx.textAlign = 'center';
ctx.fillText('a=' + (a > 0 ? '+' : '') + a + ' м/с²', ex - 20, ey + eh / 2 + arDir * 50 + (a > 0 ? -5 : 10));
}
// Label
@@ -4130,31 +4130,31 @@ function drawSeesaw25() {
// Left weight (blue, m1=4kg) at distance l1
var x1 = -l1 * scale;
var bH = 30 + m1 * 4;
ctx.fillStyle = '#3b82f6';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6');
ctx.fillRect(x1 - 20, -6 - bH, 40, bH);
ctx.strokeStyle = '#1d4ed8'; ctx.lineWidth = 2; ctx.strokeRect(x1 - 20, -6 - bH, 40, bH);
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.forceGravity:'#1d4ed8'); ctx.lineWidth = 2; ctx.strokeRect(x1 - 20, -6 - bH, 40, bH);
ctx.fillStyle = '#fff'; ctx.font = 'bold 9px Inter'; ctx.textAlign = 'center';
ctx.fillText(m1 + ' кг', x1, -6 - bH/2 + 4);
// l1 label
ctx.fillStyle = '#3b82f6'; ctx.font = '8px Inter';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.font = '8px Inter';
ctx.fillText('l₁=' + l1.toFixed(1) + 'м', x1/2, 16);
// M1 label
ctx.fillStyle = '#3b82f6'; ctx.font = 'bold 9px Inter';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.font = 'bold 9px Inter';
ctx.fillText('M₁=' + M1.toFixed(0) + ' Н·м', x1, -6 - bH - 10);
// Right weight (orange, m2=6kg) at distance l2
var x2 = l2 * scale;
var bH2 = 30 + m2 * 4;
ctx.fillStyle = '#f97316';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316');
ctx.fillRect(x2 - 20, -6 - bH2, 40, bH2);
ctx.strokeStyle = '#ea580c'; ctx.lineWidth = 2; ctx.strokeRect(x2 - 20, -6 - bH2, 40, bH2);
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#ea580c'); ctx.lineWidth = 2; ctx.strokeRect(x2 - 20, -6 - bH2, 40, bH2);
ctx.fillStyle = '#fff'; ctx.font = 'bold 9px Inter';
ctx.fillText(m2 + ' кг', x2, -6 - bH2/2 + 4);
// l2 label
ctx.fillStyle = '#f97316'; ctx.font = '8px Inter';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.font = '8px Inter';
ctx.fillText('l₂=' + l2.toFixed(1) + 'м', x2/2, 16);
// M2 label
ctx.fillStyle = '#f97316'; ctx.font = 'bold 9px Inter';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.font = 'bold 9px Inter';
ctx.fillText('M₂=' + M2.toFixed(0) + ' Н·м', x2, -6 - bH2 - 10);
ctx.textAlign = 'left';
@@ -4164,7 +4164,7 @@ function drawSeesaw25() {
var diff = Math.abs(M1 - M2);
ctx.font = 'bold 11px Inter'; ctx.textAlign = 'center';
if (diff < 1.5) {
ctx.fillStyle = '#10b981';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981');
ctx.fillText(' РАВНОВЕСИЕ! M₁ = M₂', W/2, H - 8);
} else {
ctx.fillStyle = dk ? '#94a3b8' : '#64748b';
@@ -4224,7 +4224,7 @@ function drawLever26() {
ctx.lineTo(cx - 15, pivotY + 28);
ctx.lineTo(cx + 15, pivotY + 28);
ctx.closePath();
ctx.fillStyle = '#f97316'; ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.fill();
// Beam
ctx.fillStyle = dk ? '#64748b' : '#94a3b8';
@@ -4234,31 +4234,31 @@ function drawLever26() {
// Load (left, down) — mg arrow
var arrowScale = Math.min(55, mg * 0.055);
ctx.beginPath(); ctx.moveTo(x_load, pivotY - 5); ctx.lineTo(x_load, pivotY - 5 - arrowScale);
ctx.strokeStyle = '#ef4444'; ctx.lineWidth = 2.5; ctx.stroke();
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.lineWidth = 2.5; ctx.stroke();
ctx.beginPath(); ctx.moveTo(x_load, pivotY - 5 - arrowScale);
ctx.lineTo(x_load - 6, pivotY - 5 - arrowScale + 12); ctx.lineTo(x_load + 6, pivotY - 5 - arrowScale + 12);
ctx.fillStyle = '#ef4444'; ctx.fill();
ctx.font = 'bold 9px Inter'; ctx.fillStyle = '#ef4444'; ctx.textAlign = 'center';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.fill();
ctx.font = 'bold 9px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.textAlign = 'center';
ctx.fillText('G = ' + mg + ' Н', x_load, pivotY - 5 - arrowScale - 5);
// Applied force (right, down)
var fArrow = Math.min(55, F * 0.055);
ctx.beginPath(); ctx.moveTo(x_force, pivotY - 5); ctx.lineTo(x_force, pivotY - 5 - fArrow);
ctx.strokeStyle = '#3b82f6'; ctx.lineWidth = 2.5; ctx.stroke();
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.lineWidth = 2.5; ctx.stroke();
ctx.beginPath(); ctx.moveTo(x_force, pivotY - 5 - fArrow);
ctx.lineTo(x_force - 6, pivotY - 5 - fArrow + 12); ctx.lineTo(x_force + 6, pivotY - 5 - fArrow + 12);
ctx.fillStyle = '#3b82f6'; ctx.fill();
ctx.fillStyle = '#3b82f6'; ctx.font = 'bold 9px Inter';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.font = 'bold 9px Inter';
ctx.fillText('F = ' + F.toFixed(0) + ' Н', x_force, pivotY - 5 - fArrow - 5);
// l1 and l2 labels
ctx.font = '9px Inter'; ctx.fillStyle = '#ef4444';
ctx.font = '9px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444');
ctx.fillText('l₂ = ' + l2.toFixed(1) + ' м', (x_load + cx) / 2, pivotY + 20);
ctx.fillStyle = '#3b82f6';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6');
ctx.fillText('l₁ = ' + l1.toFixed(1) + ' м', (cx + x_force) / 2, pivotY + 20);
// Gain badge
ctx.font = 'bold 11px Inter'; ctx.fillStyle = '#10b981'; ctx.textAlign = 'center';
ctx.font = 'bold 11px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.textAlign = 'center';
ctx.fillText('Выигрыш = ' + gain.toFixed(1) + '× → F в ' + gain.toFixed(1) + ' раз меньше нагрузки!', W/2, H - 8);
ctx.textAlign = 'left';
}
@@ -4335,8 +4335,8 @@ function drawIncline27() {
// Angle arc
ctx.beginPath(); ctx.arc(rightX, rightY, 28, Math.PI - rad, Math.PI);
ctx.strokeStyle = '#f59e0b'; ctx.lineWidth = 1.5; ctx.stroke();
ctx.font = 'bold 10px Inter'; ctx.fillStyle = '#f59e0b';
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.warn:'#f59e0b'); ctx.lineWidth = 1.5; ctx.stroke();
ctx.font = 'bold 10px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.warn:'#f59e0b');
ctx.fillText('α=' + alpha + '°', rightX - 52, rightY - 10);
// Block on slope
@@ -4360,10 +4360,10 @@ function drawIncline27() {
// mg arrow (vertical down)
var arLen = Math.min(55, mg * 0.25);
ctx.beginPath(); ctx.moveTo(bBlockX, bBlockY); ctx.lineTo(bBlockX, bBlockY + arLen);
ctx.strokeStyle = '#ef4444'; ctx.lineWidth = 2.5; ctx.stroke();
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.lineWidth = 2.5; ctx.stroke();
ctx.beginPath(); ctx.moveTo(bBlockX, bBlockY+arLen); ctx.lineTo(bBlockX-5,bBlockY+arLen-10); ctx.lineTo(bBlockX+5,bBlockY+arLen-10);
ctx.fillStyle = '#ef4444'; ctx.fill();
ctx.font = 'bold 9px Inter'; ctx.fillStyle = '#ef4444'; ctx.textAlign = 'left';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.fill();
ctx.font = 'bold 9px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.textAlign = 'left';
ctx.fillText('mg=' + mg + 'Н', bBlockX + 6, bBlockY + arLen - 2);
// F arrow (along slope, up)
@@ -4371,13 +4371,13 @@ function drawIncline27() {
var slopeUx = -triW / slopeLen, slopeUy = -triH / slopeLen; // up the slope
ctx.beginPath(); ctx.moveTo(bBlockX, bBlockY);
ctx.lineTo(bBlockX + slopeUx * fLen, bBlockY + slopeUy * fLen);
ctx.strokeStyle = '#3b82f6'; ctx.lineWidth = 2.5; ctx.stroke();
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.lineWidth = 2.5; ctx.stroke();
var ax2 = bBlockX + slopeUx * fLen, ay2 = bBlockY + slopeUy * fLen;
ctx.beginPath(); ctx.moveTo(ax2, ay2);
ctx.lineTo(ax2 - slopeUx * 12 + slopeUy * 5, ay2 - slopeUy * 12 - slopeUx * 5);
ctx.lineTo(ax2 - slopeUx * 12 - slopeUy * 5, ay2 - slopeUy * 12 + slopeUx * 5);
ctx.fillStyle = '#3b82f6'; ctx.fill();
ctx.font = 'bold 9px Inter'; ctx.fillStyle = '#3b82f6';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6'); ctx.fill();
ctx.font = 'bold 9px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.liquid:'#3b82f6');
ctx.fillText('F=' + F.toFixed(0) + 'Н', ax2 + 5, ay2 - 3);
// N arrow (perpendicular to slope, outward)
@@ -4385,8 +4385,8 @@ function drawIncline27() {
var nUx = triH / slopeLen, nUy = -triW / slopeLen; // outward normal
ctx.beginPath(); ctx.moveTo(bBlockX, bBlockY);
ctx.lineTo(bBlockX + nUx * nLen, bBlockY + nUy * nLen);
ctx.strokeStyle = '#10b981'; ctx.lineWidth = 2; ctx.stroke();
ctx.font = '9px Inter'; ctx.fillStyle = '#10b981';
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.lineWidth = 2; ctx.stroke();
ctx.font = '9px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981');
ctx.fillText('N=' + N.toFixed(0) + 'Н', bBlockX + nUx * nLen + 3, bBlockY + nUy * nLen - 2);
// h and l labels
@@ -4421,7 +4421,7 @@ function drawEfficiency27(eta) {
// Useful work (green)
var usefulW = Math.max(0, (eta / 100) * (W - pad*2));
if (usefulW > 0) {
ctx.fillStyle = '#10b981';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981');
ctx.beginPath();
if (ctx.roundRect) ctx.roundRect(pad, barY, usefulW, barH, 8);
else ctx.rect(pad, barY, usefulW, barH);
@@ -4430,7 +4430,7 @@ function drawEfficiency27(eta) {
// Waste (red)
var wasteW = (W - pad*2) - usefulW;
if (wasteW > 4) {
ctx.fillStyle = '#ef4444';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444');
ctx.beginPath();
ctx.rect(pad + usefulW, barY, wasteW, barH);
ctx.fill();
@@ -4479,7 +4479,7 @@ function startAnim15() {
}
// Track labels
ctx.font = 'bold 10px Inter,sans-serif';
ctx.fillStyle = '#0284c7'; ctx.fillText(' Лёд — без трения', 8, 18);
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.velocity:'#0284c7'); ctx.fillText(' Лёд — без трения', 8, 18);
ctx.fillStyle = '#78716c'; ctx.fillText(' Асфальт — трение', 8, mid + 18);
// Physics
x_ice += v_ice;
@@ -4492,27 +4492,27 @@ function startAnim15() {
var yIce = mid / 2;
ctx.beginPath(); ctx.arc(x_ice, yIce, 13, 0, Math.PI * 2);
ctx.fillStyle = '#38bdf8'; ctx.fill();
ctx.strokeStyle = '#0284c7'; ctx.lineWidth = 2; ctx.stroke();
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.velocity:'#0284c7'); ctx.lineWidth = 2; ctx.stroke();
if (Math.abs(v_ice) > 0.3) {
var d = v_ice > 0 ? 1 : -1;
ctx.beginPath(); ctx.moveTo(x_ice + d*14, yIce); ctx.lineTo(x_ice + d*28, yIce);
ctx.strokeStyle = '#0284c7'; ctx.lineWidth = 2; ctx.stroke();
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.velocity:'#0284c7'); ctx.lineWidth = 2; ctx.stroke();
ctx.beginPath(); ctx.moveTo(x_ice+d*28, yIce); ctx.lineTo(x_ice+d*21,yIce-5); ctx.lineTo(x_ice+d*21,yIce+5);
ctx.fillStyle = '#0284c7'; ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.velocity:'#0284c7'); ctx.fill();
}
ctx.font = 'bold 9px Inter'; ctx.fillStyle = '#0284c7';
ctx.font = 'bold 9px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.velocity:'#0284c7');
ctx.fillText('v=' + Math.abs(v_ice).toFixed(1), x_ice - 12, yIce + 24);
// Asphalt ball (orange)
var yAsp = mid + (H - mid) / 2;
ctx.beginPath(); ctx.arc(x_asp, yAsp, 13, 0, Math.PI * 2);
ctx.fillStyle = '#fb923c'; ctx.fill();
ctx.strokeStyle = '#ea580c'; ctx.lineWidth = 2; ctx.stroke();
ctx.font = 'bold 9px Inter'; ctx.fillStyle = '#ea580c';
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#ea580c'); ctx.lineWidth = 2; ctx.stroke();
ctx.font = 'bold 9px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#ea580c');
if (v_asp > 0.3) {
ctx.beginPath(); ctx.moveTo(x_asp+14,yAsp); ctx.lineTo(x_asp+28,yAsp);
ctx.strokeStyle = '#ea580c'; ctx.lineWidth = 2; ctx.stroke();
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#ea580c'); ctx.lineWidth = 2; ctx.stroke();
ctx.beginPath(); ctx.moveTo(x_asp+28,yAsp); ctx.lineTo(x_asp+21,yAsp-5); ctx.lineTo(x_asp+21,yAsp+5);
ctx.fillStyle = '#ea580c'; ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#ea580c'); ctx.fill();
ctx.fillText('v=' + v_asp.toFixed(1), x_asp - 12, yAsp - 17);
} else {
ctx.fillText(' СТОИТ', x_asp - 18, yAsp - 17);
@@ -4590,25 +4590,25 @@ function startAnim17() {
// Force arrow
var aLen = Math.min(55, F17 * 1.1);
ctx.beginPath(); ctx.moveTo(x17 + bW, by + bH / 2); ctx.lineTo(x17 + bW + aLen, by + bH / 2);
ctx.strokeStyle = '#ef4444'; ctx.lineWidth = 3; ctx.stroke();
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.lineWidth = 3; ctx.stroke();
ctx.beginPath(); var ax = x17 + bW + aLen;
ctx.moveTo(ax, by+bH/2); ctx.lineTo(ax-11,by+bH/2-5); ctx.lineTo(ax-11,by+bH/2+5);
ctx.fillStyle = '#ef4444'; ctx.fill();
ctx.font = 'bold 9px Inter'; ctx.fillStyle = '#ef4444';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.fill();
ctx.font = 'bold 9px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444');
ctx.fillText('F=' + F17 + 'Н', x17 + bW + 2, by - 4);
// Velocity
if (v17 > 0.5) {
ctx.font = 'bold 9px Inter'; ctx.fillStyle = dk ? '#818cf8' : '#4338ca';
ctx.fillText('v = ' + v17.toFixed(1) + ' м/с →', x17, by - 16);
}
ctx.font = 'bold 9px Inter'; ctx.fillStyle = '#10b981';
ctx.font = 'bold 9px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981');
ctx.fillText('a = ' + a17.toFixed(1) + ' м/с²', 6, 14);
// Physics
v17 += a17 * 0.06; x17 += v17 * 0.7; t17++;
if (x17 < W - 70 && t17 < 280) {
anim17Id = requestAnimationFrame(fr17);
} else {
ctx.font = 'bold 12px Inter'; ctx.fillStyle = '#10b981'; ctx.textAlign = 'center';
ctx.font = 'bold 12px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.textAlign = 'center';
ctx.fillText(' v = ' + v17.toFixed(0) + ' м/с | a = ' + a17.toFixed(1) + ' м/с²', W/2, H/2 - 5);
ctx.font = '10px Inter'; ctx.fillStyle = dk?'#94a3b8':'#64748b';
ctx.fillText('Поменяй ползунки и нажми «Запустить снова»', W/2, H/2 + 12);
@@ -4647,11 +4647,11 @@ function startAnim18() {
if (phase18 === 0) {
// Static with spring
ctx.fillStyle = '#6366f1'; ctx.fillRect(x1 - 34, y, 34, bH);
ctx.fillStyle = '#f97316'; ctx.fillRect(x2, y, 44, bH);
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.fillRect(x2, y, 44, bH);
ctx.fillStyle = '#fff'; ctx.font = 'bold 9px Inter'; ctx.textAlign = 'center';
ctx.fillText(m18a + ' кг', x1 - 17, y + bH/2 + 4);
ctx.fillText(m18b + ' кг', x2 + 22, y + bH/2 + 4); ctx.textAlign = 'left';
ctx.strokeStyle = '#10b981'; ctx.lineWidth = 2; ctx.beginPath();
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.lineWidth = 2; ctx.beginPath();
for (var ss = x1; ss < x2; ss += 4) {
ctx.lineTo(ss, y + (ss % 8 < 4 ? bH * 0.25 : bH * 0.75));
}
@@ -4670,7 +4670,7 @@ function startAnim18() {
}
x1 += v1; x2 += v2;
ctx.fillStyle = '#6366f1'; ctx.fillRect(x1 - 34, y, 34, bH);
ctx.fillStyle = '#f97316'; ctx.fillRect(x2, y, 44, bH);
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#f97316'); ctx.fillRect(x2, y, 44, bH);
ctx.fillStyle = '#fff'; ctx.font = 'bold 9px Inter'; ctx.textAlign = 'center';
ctx.fillText(m18a + ' кг', x1 - 17, y + bH/2 + 4);
ctx.fillText(m18b + ' кг', x2 + 22, y + bH/2 + 4); ctx.textAlign = 'left';
@@ -4740,11 +4740,11 @@ function drawSpring19() {
var arLen = Math.min(65, F * 0.55);
if (arLen > 4) {
ctx.beginPath(); ctx.moveTo(bX, sy); ctx.lineTo(bX - arLen, sy);
ctx.strokeStyle = '#ef4444'; ctx.lineWidth = 2.5; ctx.stroke();
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.lineWidth = 2.5; ctx.stroke();
ctx.beginPath();
ctx.moveTo(bX - arLen, sy); ctx.lineTo(bX - arLen + 11, sy - 5); ctx.lineTo(bX - arLen + 11, sy + 5);
ctx.fillStyle = '#ef4444'; ctx.fill();
ctx.font = 'bold 9px Inter'; ctx.fillStyle = '#ef4444';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.fill();
ctx.font = 'bold 9px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444');
ctx.fillText('F = ' + F.toFixed(1) + ' Н', bX - arLen - 2, sy + 15);
}
ctx.font = '9px Inter'; ctx.fillStyle = dk ? '#94a3b8' : '#64748b';
@@ -4869,15 +4869,15 @@ function startAnim21() {
}
// Drop ball (orange)
ctx.beginPath(); ctx.arc(startX21, fallY_px, 11, 0, Math.PI * 2);
ctx.fillStyle = '#fb923c'; ctx.fill(); ctx.strokeStyle = '#ea580c'; ctx.lineWidth = 2; ctx.stroke();
ctx.fillStyle = '#fb923c'; ctx.fill(); ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#ea580c'); ctx.lineWidth = 2; ctx.stroke();
// Thrown ball (blue)
ctx.beginPath(); ctx.arc(thrX_px, thrY_px, 11, 0, Math.PI * 2);
ctx.fillStyle = '#38bdf8'; ctx.fill(); ctx.strokeStyle = '#0284c7'; ctx.lineWidth = 2; ctx.stroke();
ctx.fillStyle = '#38bdf8'; ctx.fill(); ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.velocity:'#0284c7'); ctx.lineWidth = 2; ctx.stroke();
// Labels (first frame only)
if (t21 < 4) {
ctx.font = 'bold 9px Inter'; ctx.fillStyle = '#ea580c';
ctx.font = 'bold 9px Inter'; ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#ea580c');
ctx.fillText(' падение', startX21 + 14, fallY_px);
ctx.fillStyle = '#0284c7';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.velocity:'#0284c7');
ctx.fillText(' бросок →', thrX_px + 14, thrY_px - 4);
}
// Velocity components on thrown ball
@@ -4885,13 +4885,13 @@ function startAnim21() {
var vxL = Math.min(22, v0_21 * scaleX21 * 0.3);
var vyL = Math.min(22, g21 * simT21 * scaleY21 * 0.3);
ctx.beginPath(); ctx.moveTo(thrX_px+12, thrY_px); ctx.lineTo(thrX_px+12+vxL, thrY_px);
ctx.strokeStyle = '#0284c7'; ctx.lineWidth = 1.5; ctx.stroke();
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.velocity:'#0284c7'); ctx.lineWidth = 1.5; ctx.stroke();
ctx.beginPath(); ctx.moveTo(thrX_px, thrY_px+12); ctx.lineTo(thrX_px, thrY_px+12+vyL);
ctx.strokeStyle = '#7c3aed'; ctx.lineWidth = 1.5; ctx.stroke();
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.forceFriction:'#7c3aed'); ctx.lineWidth = 1.5; ctx.stroke();
}
if (frac21 >= 1.0) landed21 = true;
if (landed21) {
ctx.fillStyle = '#10b981'; ctx.font = 'bold 13px Inter'; ctx.textAlign = 'center';
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.font = 'bold 13px Inter'; ctx.textAlign = 'center';
ctx.fillText(' Упали ОДНОВРЕМЕННО! t = ' + tTotal.toFixed(1) + ' с', W/2, groundY21 - 16);
ctx.font = '10px Inter'; ctx.fillStyle = dk ? '#94a3b8' : '#475569';
ctx.fillText('Горизонтальная скорость не влияет на время падения!', W/2, groundY21 - 3);
@@ -4946,12 +4946,12 @@ function drawEquilibrium28(typeIdx, ang) {
ctx.beginPath(); ctx.arc(px, pivY, 6, 0, Math.PI * 2);
ctx.fillStyle = dark ? '#64748b' : '#94a3b8'; ctx.fill();
ctx.beginPath(); ctx.arc(bx, by, 18, 0, Math.PI * 2);
ctx.fillStyle = '#1d4ed8'; ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.forceGravity:'#1d4ed8'); ctx.fill();
ctx.fillStyle = '#fff'; ctx.font = 'bold 10px Inter'; ctx.textAlign = 'center';
ctx.fillText('ЦТ', bx, by + 4);
// Restore arrow
if (Math.abs(ang) > 5) {
ctx.strokeStyle = '#10b981'; ctx.lineWidth = 2;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.lineWidth = 2;
ctx.beginPath(); ctx.arc(px, pivY, rodLen + 15, Math.PI/2, Math.PI/2 - rad * 0.5, rad > 0);
ctx.stroke();
}
@@ -5039,18 +5039,18 @@ function drawArchimedes29(rhoT, V, Ft, Farch) {
ctx.fillText(rhoT, cx, blockY + blockSize / 2 + 4);
// Archimedes arrow (up, green)
var sc = Math.max(10, Math.min(60, Farch / 10));
ctx.strokeStyle = '#10b981'; ctx.lineWidth = 2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.lineWidth = 2.5;
ctx.beginPath(); ctx.moveTo(cx + 35, blockY + blockSize); ctx.lineTo(cx + 35, blockY + blockSize - sc); ctx.stroke();
ctx.beginPath(); ctx.moveTo(cx + 32, blockY + blockSize - sc + 6); ctx.lineTo(cx + 35, blockY + blockSize - sc - 4); ctx.lineTo(cx + 38, blockY + blockSize - sc + 6);
ctx.fillStyle = '#10b981'; ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.force:'#10b981'); ctx.fill();
ctx.font = '10px Inter'; ctx.textAlign = 'left';
ctx.fillText('F_выт=' + Farch.toFixed(0) + 'Н', cx + 40, blockY + blockSize - sc / 2 + 4);
// Weight arrow (down, red)
var scFt = Math.max(10, Math.min(60, Ft / 10));
ctx.strokeStyle = '#ef4444'; ctx.lineWidth = 2.5;
ctx.strokeStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.lineWidth = 2.5;
ctx.beginPath(); ctx.moveTo(cx - 35, blockY); ctx.lineTo(cx - 35, blockY + scFt); ctx.stroke();
ctx.beginPath(); ctx.moveTo(cx - 38, blockY + scFt - 6); ctx.lineTo(cx - 35, blockY + scFt + 4); ctx.lineTo(cx - 32, blockY + scFt - 6);
ctx.fillStyle = '#ef4444'; ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.plotPrimary:'#ef4444'); ctx.fill();
ctx.font = '10px Inter'; ctx.textAlign = 'right';
ctx.fillText('F_т=' + Ft.toFixed(0) + 'Н', cx - 40, blockY + scFt / 2 + 4);
ctx.textAlign = 'left';
@@ -5584,7 +5584,7 @@ function drawLab11() {
const b1x = bpx - Math.sin(ang) * (BR + 1);
const b1y = bpy + Math.cos(ang) * (BR + 1);
ctx.beginPath(); ctx.arc(b1x, b1y, BR, 0, Math.PI * 2);
ctx.fillStyle = '#2563eb'; ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.displacement:'#2563eb'); ctx.fill();
ctx.strokeStyle = '#93c5fd'; ctx.lineWidth = 1.5; ctx.stroke();
ctx.fillStyle = '#fff'; ctx.font = 'bold 8px Inter'; ctx.textAlign = 'center';
ctx.fillText('1', b1x, b1y + 3);
@@ -5592,7 +5592,7 @@ function drawLab11() {
// ── Ball 2 at edge ────────────────────────────────────────────
const b2x = EX + BR + 2, b2y = TY - BR;
ctx.beginPath(); ctx.arc(b2x, b2y, BR, 0, Math.PI * 2);
ctx.fillStyle = '#ea580c'; ctx.fill();
ctx.fillStyle = (window.PHYS9_COLORS?window.PHYS9_COLORS.acceleration:'#ea580c'); ctx.fill();
ctx.strokeStyle = '#fbbf24'; ctx.lineWidth = 1.5; ctx.stroke();
ctx.fillStyle = '#fff'; ctx.font = 'bold 8px Inter'; ctx.textAlign = 'center';
ctx.fillText('2', b2x, b2y + 3);