33a91900a8
Полная инфраструктура курса «Физика 8» (Исаченкова, 2018): - physics_8_hub.html: палитра violet/indigo, 3 главы + ЛР + финал курса с 10 интегрированными боссами и ачивкой «Магистр физики 8» (+150 XP) - physics_8_ch1.html (Тепловые, §§1–11): красный акцент - physics_8_ch2.html (Электромагнитные, §§12–31): янтарный акцент - physics_8_ch3.html (Световые, §§32–40): голубой акцент - physics_8_lab.html (7 ЛР): зелёный акцент - Расширение phys.js: tempColor, thermometer, calorimeter, createHeatBar, phaseGraphTT, Rseries, Rparallel - Новый модуль optics.js: ray, refractRay, reflectRay, mirrorPlane, mirrorSpherical, thinLens, buildLensImage, goldenRays, eyeDiagram, lightObject, shadowTriangle - Миграция 037: replace legacy children (thermal/electro/optics) на physics-8-ch1/ch2/ch3 + physics-8-lab; обновлён hub до 47 пунктов BUILDERS всех § рендерят stub с указанием Phase/Wave из PLAN_PHYSICS_8.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
403 lines
18 KiB
JavaScript
403 lines
18 KiB
JavaScript
// phys.js — модуль физических хелперов для учебника Физика 10
|
||
// Экспорт в window.PHYS = { ... }
|
||
(function(){
|
||
'use strict';
|
||
|
||
// === Стрелка вектора (2D) ===
|
||
function drawArrow(x1, y1, x2, y2, color, width, headSize) {
|
||
width = width || 2;
|
||
headSize = headSize || 10;
|
||
const dx = x2 - x1, dy = y2 - y1;
|
||
const len = Math.sqrt(dx*dx + dy*dy);
|
||
if (len < 1e-6) return '';
|
||
const ux = dx / len, uy = dy / len;
|
||
const px = -uy, py = ux;
|
||
const h = headSize, w = headSize * 0.6;
|
||
const bx = x2 - ux*h, by = y2 - uy*h;
|
||
const lx = bx + px*w, ly = by + py*w;
|
||
const rx = bx - px*w, ry = by - py*w;
|
||
return `<line x1="${x1.toFixed(1)}" y1="${y1.toFixed(1)}" x2="${bx.toFixed(1)}" y2="${by.toFixed(1)}" stroke="${color}" stroke-width="${width}" stroke-linecap="round"/>`
|
||
+ `<polygon points="${x2.toFixed(1)},${y2.toFixed(1)} ${lx.toFixed(1)},${ly.toFixed(1)} ${rx.toFixed(1)},${ry.toFixed(1)}" fill="${color}"/>`;
|
||
}
|
||
|
||
// === Линии электрического поля от точечного заряда ===
|
||
function fieldLinesPointCharge(cx, cy, sign, scale, numLines) {
|
||
numLines = numLines || 16;
|
||
scale = scale || 80;
|
||
let s = '';
|
||
const color = sign > 0 ? '#dc2626' : '#2563eb';
|
||
for (let i = 0; i < numLines; i++) {
|
||
const a = 2 * Math.PI * i / numLines;
|
||
const r1 = 18, r2 = scale;
|
||
const x1 = cx + r1*Math.cos(a), y1 = cy + r1*Math.sin(a);
|
||
const x2 = cx + r2*Math.cos(a), y2 = cy + r2*Math.sin(a);
|
||
if (sign > 0) s += drawArrow(x1, y1, x2, y2, color, 1.4, 7);
|
||
else s += drawArrow(x2, y2, x1, y1, color, 1.4, 7);
|
||
}
|
||
return s;
|
||
}
|
||
|
||
// === Обозначение заряда (кружок с +/-) ===
|
||
function chargeMark(cx, cy, sign, r, label) {
|
||
r = r || 14;
|
||
const color = sign > 0 ? '#dc2626' : '#2563eb';
|
||
const fill = sign > 0 ? '#fecaca' : '#bfdbfe';
|
||
let s = '';
|
||
s += `<circle cx="${cx}" cy="${cy}" r="${r}" fill="${fill}" stroke="${color}" stroke-width="2"/>`;
|
||
if (sign > 0) {
|
||
s += `<line x1="${cx-r*0.5}" y1="${cy}" x2="${cx+r*0.5}" y2="${cy}" stroke="${color}" stroke-width="2.5"/>`;
|
||
s += `<line x1="${cx}" y1="${cy-r*0.5}" x2="${cx}" y2="${cy+r*0.5}" stroke="${color}" stroke-width="2.5"/>`;
|
||
} else {
|
||
s += `<line x1="${cx-r*0.5}" y1="${cy}" x2="${cx+r*0.5}" y2="${cy}" stroke="${color}" stroke-width="2.5"/>`;
|
||
}
|
||
if (label) {
|
||
s += `<text x="${cx+r+4}" y="${cy+4}" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="${color}">${label}</text>`;
|
||
}
|
||
return s;
|
||
}
|
||
|
||
// === Магнитное поле сквозь экран (сетка крестиков или точек) ===
|
||
function magneticFieldGrid(x0, y0, w, h, cols, rows, direction) {
|
||
// direction: 'in' = крест (× — вошло в плоскость), 'out' = точка (• — вышло)
|
||
let s = '';
|
||
const dx = w / (cols - 1), dy = h / (rows - 1);
|
||
const color = '#7c3aed';
|
||
for (let i = 0; i < cols; i++) {
|
||
for (let j = 0; j < rows; j++) {
|
||
const cx = x0 + i * dx, cy = y0 + j * dy;
|
||
s += `<circle cx="${cx}" cy="${cy}" r="6" fill="white" stroke="${color}" stroke-width="1.3"/>`;
|
||
if (direction === 'in') {
|
||
s += `<line x1="${cx-4}" y1="${cy-4}" x2="${cx+4}" y2="${cy+4}" stroke="${color}" stroke-width="1.5"/>`;
|
||
s += `<line x1="${cx-4}" y1="${cy+4}" x2="${cx+4}" y2="${cy-4}" stroke="${color}" stroke-width="1.5"/>`;
|
||
} else {
|
||
s += `<circle cx="${cx}" cy="${cy}" r="2.2" fill="${color}"/>`;
|
||
}
|
||
}
|
||
}
|
||
return s;
|
||
}
|
||
|
||
// === Молекула газа (частица) ===
|
||
function molecule(x, y, r, color) {
|
||
r = r || 4;
|
||
color = color || '#2563eb';
|
||
return `<circle cx="${x.toFixed(1)}" cy="${y.toFixed(1)}" r="${r}" fill="${color}" stroke="#0f172a" stroke-width="0.6"/>`;
|
||
}
|
||
|
||
// === Симуляция газа (упругое столкновение со стенками) ===
|
||
function createGasSim(opts) {
|
||
opts = opts || {};
|
||
const N = opts.N || 30;
|
||
const W = opts.W || 320;
|
||
const H = opts.H || 220;
|
||
const baseSpeed = opts.speed || 60; // px/s
|
||
const r = opts.r || 4;
|
||
const particles = [];
|
||
for (let i = 0; i < N; i++) {
|
||
particles.push({
|
||
x: r + Math.random() * (W - 2*r),
|
||
y: r + Math.random() * (H - 2*r),
|
||
vx: (Math.random() - 0.5) * 2 * baseSpeed,
|
||
vy: (Math.random() - 0.5) * 2 * baseSpeed
|
||
});
|
||
}
|
||
return {
|
||
W: W, H: H, r: r, particles: particles,
|
||
step(dt) {
|
||
for (const p of particles) {
|
||
p.x += p.vx * dt; p.y += p.vy * dt;
|
||
if (p.x < r) { p.x = r; p.vx = -p.vx; }
|
||
if (p.x > W - r) { p.x = W - r; p.vx = -p.vx; }
|
||
if (p.y < r) { p.y = r; p.vy = -p.vy; }
|
||
if (p.y > H - r) { p.y = H - r; p.vy = -p.vy; }
|
||
}
|
||
},
|
||
render(color) {
|
||
color = color || '#2563eb';
|
||
return particles.map(p => molecule(p.x, p.y, r, color)).join('');
|
||
},
|
||
setSpeed(scale) {
|
||
for (const p of particles) { p.vx *= scale; p.vy *= scale; }
|
||
}
|
||
};
|
||
}
|
||
|
||
// === Электрические схемы: компоненты ===
|
||
// orientation: 'h' (горизонтально, по умолчанию) или 'v' (вертикально)
|
||
function batteryEMF(x, y, EMF, orientation) {
|
||
orientation = orientation || 'h';
|
||
let s = '';
|
||
if (orientation === 'h') {
|
||
s += `<line x1="${x-3}" y1="${y-18}" x2="${x-3}" y2="${y+18}" stroke="#0f172a" stroke-width="2.5"/>`;
|
||
s += `<line x1="${x+3}" y1="${y-9}" x2="${x+3}" y2="${y+9}" stroke="#0f172a" stroke-width="2.5"/>`;
|
||
s += `<text x="${x-3}" y="${y-24}" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">+</text>`;
|
||
s += `<text x="${x+3}" y="${y-24}" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0f172a">−</text>`;
|
||
if (EMF !== undefined) s += `<text x="${x}" y="${y+34}" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#0f172a">ε = ${EMF}</text>`;
|
||
} else {
|
||
s += `<line x1="${x-18}" y1="${y-3}" x2="${x+18}" y2="${y-3}" stroke="#0f172a" stroke-width="2.5"/>`;
|
||
s += `<line x1="${x-9}" y1="${y+3}" x2="${x+9}" y2="${y+3}" stroke="#0f172a" stroke-width="2.5"/>`;
|
||
if (EMF !== undefined) s += `<text x="${x+24}" y="${y+4}" font-family="JetBrains Mono,monospace" font-size="12" fill="#0f172a">ε = ${EMF}</text>`;
|
||
}
|
||
return s;
|
||
}
|
||
|
||
function resistor(x, y, R, orientation) {
|
||
orientation = orientation || 'h';
|
||
let s = '';
|
||
if (orientation === 'h') {
|
||
s += `<rect x="${x-20}" y="${y-7}" width="40" height="14" fill="white" stroke="#0f172a" stroke-width="1.6"/>`;
|
||
if (R !== undefined) s += `<text x="${x}" y="${y+25}" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#0f172a">R = ${R}</text>`;
|
||
} else {
|
||
s += `<rect x="${x-7}" y="${y-20}" width="14" height="40" fill="white" stroke="#0f172a" stroke-width="1.6"/>`;
|
||
if (R !== undefined) s += `<text x="${x+18}" y="${y+4}" font-family="JetBrains Mono,monospace" font-size="12" fill="#0f172a">R = ${R}</text>`;
|
||
}
|
||
return s;
|
||
}
|
||
|
||
function capacitorSymbol(x, y, C, orientation) {
|
||
orientation = orientation || 'h';
|
||
let s = '';
|
||
if (orientation === 'h') {
|
||
s += `<line x1="${x-3}" y1="${y-14}" x2="${x-3}" y2="${y+14}" stroke="#0f172a" stroke-width="2.5"/>`;
|
||
s += `<line x1="${x+3}" y1="${y-14}" x2="${x+3}" y2="${y+14}" stroke="#0f172a" stroke-width="2.5"/>`;
|
||
if (C !== undefined) s += `<text x="${x}" y="${y+30}" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#0f172a">C = ${C}</text>`;
|
||
}
|
||
return s;
|
||
}
|
||
|
||
function ammeterSymbol(x, y, r) {
|
||
r = r || 14;
|
||
return `<circle cx="${x}" cy="${y}" r="${r}" fill="white" stroke="#0f172a" stroke-width="1.6"/>`
|
||
+ `<text x="${x}" y="${y+5}" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#d97706">A</text>`;
|
||
}
|
||
|
||
function voltmeterSymbol(x, y, r) {
|
||
r = r || 14;
|
||
return `<circle cx="${x}" cy="${y}" r="${r}" fill="white" stroke="#0f172a" stroke-width="1.6"/>`
|
||
+ `<text x="${x}" y="${y+5}" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#2563eb">V</text>`;
|
||
}
|
||
|
||
function lightbulbSymbol(x, y, r) {
|
||
r = r || 14;
|
||
let s = '';
|
||
s += `<circle cx="${x}" cy="${y}" r="${r}" fill="#fef3c7" stroke="#0f172a" stroke-width="1.6"/>`;
|
||
s += `<line x1="${x-r*0.7}" y1="${y-r*0.7}" x2="${x+r*0.7}" y2="${y+r*0.7}" stroke="#0f172a" stroke-width="1.4"/>`;
|
||
s += `<line x1="${x-r*0.7}" y1="${y+r*0.7}" x2="${x+r*0.7}" y2="${y-r*0.7}" stroke="#0f172a" stroke-width="1.4"/>`;
|
||
return s;
|
||
}
|
||
|
||
function inductorSymbol(x, y, L, orientation) {
|
||
orientation = orientation || 'h';
|
||
let s = '';
|
||
if (orientation === 'h') {
|
||
for (let i = 0; i < 4; i++) {
|
||
const cx = x - 15 + i * 10;
|
||
s += `<path d="M ${cx-5} ${y} A 5 5 0 0 1 ${cx+5} ${y}" fill="none" stroke="#0f172a" stroke-width="1.6"/>`;
|
||
}
|
||
if (L !== undefined) s += `<text x="${x}" y="${y+22}" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#0f172a">L = ${L}</text>`;
|
||
}
|
||
return s;
|
||
}
|
||
|
||
function wire(x1, y1, x2, y2) {
|
||
return `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="#0f172a" stroke-width="1.6"/>`;
|
||
}
|
||
|
||
// === Эталонные константы ===
|
||
const CONST = {
|
||
k: 9e9, // Кулона
|
||
e: 1.6e-19, // элементарный заряд
|
||
eps0: 8.85e-12, // электрическая постоянная
|
||
kB: 1.38e-23, // Больцмана
|
||
NA: 6.022e23, // Авогадро
|
||
R: 8.314, // универсальная газовая
|
||
c: 3e8, // скорость света
|
||
g: 9.8, // ускорение свободного падения
|
||
atm: 101325, // 1 атм в Па
|
||
T0: 273.15 // ноль Цельсия в К
|
||
};
|
||
|
||
// === Конвертеры единиц ===
|
||
function celsiusToKelvin(t) { return t + 273.15; }
|
||
function kelvinToCelsius(T) { return T - 273.15; }
|
||
function atmToPa(p) { return p * 101325; }
|
||
function paToAtm(p) { return p / 101325; }
|
||
function litersToM3(V) { return V / 1000; }
|
||
function m3ToLiters(V) { return V * 1000; }
|
||
|
||
// === Расширения для Физики 8 (тепловые явления) ===
|
||
|
||
// === Цвет по температуре: 240° (синий) → 0° (красный) ===
|
||
function tempColor(t, tMin, tMax) {
|
||
const u = Math.max(0, Math.min(1, (t - tMin) / (tMax - tMin)));
|
||
const hue = 240 * (1 - u);
|
||
return `hsl(${hue.toFixed(0)},72%,52%)`;
|
||
}
|
||
|
||
// === Термометр (вертикальная шкала, столбик ртути по температуре) ===
|
||
function thermometer(x, y, h, tMin, tMax, tValue) {
|
||
// x, y — верх корпуса; h — высота столбика
|
||
const reservoirR = 11;
|
||
const tubeW = 8;
|
||
const u = Math.max(0, Math.min(1, (tValue - tMin) / (tMax - tMin)));
|
||
const fillH = h * u;
|
||
const color = tempColor(tValue, tMin, tMax);
|
||
let s = '';
|
||
// Корпус (стекло)
|
||
s += `<rect x="${x - tubeW/2}" y="${y}" width="${tubeW}" height="${h}" rx="${tubeW/2}" fill="#fff" stroke="#0f172a" stroke-width="1.4"/>`;
|
||
// Столбик ртути
|
||
s += `<rect x="${x - tubeW/2 + 1.5}" y="${y + h - fillH}" width="${tubeW - 3}" height="${fillH}" rx="${(tubeW-3)/2}" fill="${color}"/>`;
|
||
// Резервуар
|
||
s += `<circle cx="${x}" cy="${y + h + reservoirR - 1}" r="${reservoirR}" fill="${color}" stroke="#0f172a" stroke-width="1.4"/>`;
|
||
// Деления (5)
|
||
for (let i = 0; i <= 5; i++) {
|
||
const ty = y + h * i / 5;
|
||
const tv = tMax - (tMax - tMin) * i / 5;
|
||
s += `<line x1="${x + tubeW/2 + 1}" y1="${ty}" x2="${x + tubeW/2 + 6}" y2="${ty}" stroke="#0f172a" stroke-width="1"/>`;
|
||
s += `<text x="${x + tubeW/2 + 9}" y="${ty + 3.5}" font-family="JetBrains Mono,monospace" font-size="10" fill="#0f172a">${tv.toFixed(0)}</text>`;
|
||
}
|
||
// Подпись текущего значения
|
||
s += `<text x="${x - tubeW/2 - 4}" y="${y - 4}" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="${color}">${tValue.toFixed(0)} °C</text>`;
|
||
return s;
|
||
}
|
||
|
||
// === Калориметр-стакан с жидкостью ===
|
||
function calorimeter(x, y, w, h, fillFrac, liquidColor) {
|
||
fillFrac = Math.max(0, Math.min(1, fillFrac || 0.6));
|
||
liquidColor = liquidColor || '#60a5fa';
|
||
const wall = 4;
|
||
const innerH = h - wall;
|
||
const liqH = innerH * fillFrac;
|
||
let s = '';
|
||
// Внешний контур
|
||
s += `<path d="M ${x} ${y} L ${x} ${y+h} Q ${x} ${y+h+6} ${x+8} ${y+h+6} L ${x+w-8} ${y+h+6} Q ${x+w} ${y+h+6} ${x+w} ${y+h} L ${x+w} ${y} L ${x+w-wall} ${y} L ${x+w-wall} ${y+h-2} L ${x+wall} ${y+h-2} L ${x+wall} ${y} Z" fill="#e0f2fe" stroke="#0f172a" stroke-width="1.5"/>`;
|
||
// Жидкость
|
||
s += `<rect x="${x+wall+1}" y="${y + innerH - liqH + wall/2}" width="${w - 2*wall - 2}" height="${liqH}" fill="${liquidColor}" opacity="0.78"/>`;
|
||
// Мениск
|
||
s += `<line x1="${x+wall+1}" y1="${y + innerH - liqH + wall/2}" x2="${x+w-wall-1}" y2="${y + innerH - liqH + wall/2}" stroke="${liquidColor}" stroke-width="1.6" opacity="0.95"/>`;
|
||
return s;
|
||
}
|
||
|
||
// === Симуляция теплопроводности по стержню (одномерное уравнение тепла) ===
|
||
// Возвращает объект с .step(dt), .render(x, y, w, h), .reset(), .setTHot(t), .setTCold(t)
|
||
function createHeatBar(opts) {
|
||
opts = opts || {};
|
||
const N = opts.N || 30;
|
||
const tHot = opts.tHot != null ? opts.tHot : 200;
|
||
const tCold = opts.tCold != null ? opts.tCold : 0;
|
||
const alpha = opts.alpha || 0.25; // коэф. диффузии (отн. ед.)
|
||
const T = new Array(N);
|
||
for (let i = 0; i < N; i++) T[i] = tCold;
|
||
return {
|
||
N: N, alpha: alpha, T: T,
|
||
_tHot: tHot, _tCold: tCold,
|
||
setTHot(v) { this._tHot = v; },
|
||
setTCold(v) { this._tCold = v; },
|
||
reset() {
|
||
for (let i = 0; i < this.N; i++) this.T[i] = this._tCold;
|
||
},
|
||
step(dt) {
|
||
// Конечно-разностное уравнение теплопроводности с граничными условиями Дирихле.
|
||
const Tnew = new Array(this.N);
|
||
Tnew[0] = this._tHot;
|
||
Tnew[this.N - 1] = this._tCold;
|
||
for (let i = 1; i < this.N - 1; i++) {
|
||
Tnew[i] = this.T[i] + this.alpha * dt * (this.T[i-1] - 2*this.T[i] + this.T[i+1]);
|
||
}
|
||
this.T = Tnew;
|
||
},
|
||
render(x, y, w, h) {
|
||
const segW = w / this.N;
|
||
const tMin = Math.min(this._tCold, this._tHot);
|
||
const tMax = Math.max(this._tCold, this._tHot);
|
||
let s = '';
|
||
for (let i = 0; i < this.N; i++) {
|
||
const c = tempColor(this.T[i], tMin, tMax);
|
||
s += `<rect x="${(x + i*segW).toFixed(1)}" y="${y}" width="${(segW + 0.5).toFixed(1)}" height="${h}" fill="${c}"/>`;
|
||
}
|
||
// Контур стержня
|
||
s += `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="none" stroke="#0f172a" stroke-width="1.5"/>`;
|
||
return s;
|
||
}
|
||
};
|
||
}
|
||
|
||
// === График фазовых переходов T(t) с горизонтальными плато ===
|
||
// points: массив сегментов [{tStart, tEnd, Tstart, Tend, label?}]
|
||
// W, H, pad — размеры графика; tMaxAll, TminAll, TmaxAll — диапазоны
|
||
function phaseGraphTT(W, H, pad, points, tMaxAll, TminAll, TmaxAll) {
|
||
const toX = t => pad + (W - 2*pad) * t / tMaxAll;
|
||
const toY = T => H - pad - (H - 2*pad) * (T - TminAll) / (TmaxAll - TminAll);
|
||
let s = '';
|
||
// Оси
|
||
s += `<line x1="${pad}" y1="${H-pad}" x2="${W-pad}" y2="${H-pad}" stroke="#0f172a" stroke-width="1.5"/>`;
|
||
s += `<line x1="${pad}" y1="${pad}" x2="${pad}" y2="${H-pad}" stroke="#0f172a" stroke-width="1.5"/>`;
|
||
s += `<text x="${W-pad+4}" y="${H-pad+4}" font-family="Inter,sans-serif" font-size="11" fill="#0f172a">t</text>`;
|
||
s += `<text x="${pad-4}" y="${pad-4}" text-anchor="end" font-family="Inter,sans-serif" font-size="11" fill="#0f172a">T,°C</text>`;
|
||
// Сегменты
|
||
let d = '';
|
||
let first = true;
|
||
for (const seg of points) {
|
||
if (first) { d += `M ${toX(seg.tStart).toFixed(1)} ${toY(seg.Tstart).toFixed(1)} `; first = false; }
|
||
d += `L ${toX(seg.tEnd).toFixed(1)} ${toY(seg.Tend).toFixed(1)} `;
|
||
}
|
||
s += `<path d="${d}" stroke="#dc2626" stroke-width="2.5" fill="none" stroke-linejoin="round" stroke-linecap="round"/>`;
|
||
// Подписи сегментов
|
||
for (const seg of points) {
|
||
if (seg.label) {
|
||
const mx = (toX(seg.tStart) + toX(seg.tEnd)) / 2;
|
||
const my = (toY(seg.Tstart) + toY(seg.Tend)) / 2 - 8;
|
||
s += `<text x="${mx.toFixed(1)}" y="${my.toFixed(1)}" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="600" fill="#475569">${seg.label}</text>`;
|
||
}
|
||
}
|
||
return { svg: s, toX: toX, toY: toY };
|
||
}
|
||
|
||
// === Электронные хелперы для электрических задач ===
|
||
// Параллельное и последовательное сопротивление
|
||
function Rseries() {
|
||
let R = 0;
|
||
for (let i = 0; i < arguments.length; i++) R += arguments[i];
|
||
return R;
|
||
}
|
||
function Rparallel() {
|
||
let inv = 0;
|
||
for (let i = 0; i < arguments.length; i++) {
|
||
if (arguments[i] > 0) inv += 1 / arguments[i];
|
||
}
|
||
return inv > 0 ? 1 / inv : Infinity;
|
||
}
|
||
|
||
// === Экспорт ===
|
||
window.PHYS = {
|
||
tempColor: tempColor,
|
||
thermometer: thermometer,
|
||
calorimeter: calorimeter,
|
||
createHeatBar: createHeatBar,
|
||
phaseGraphTT: phaseGraphTT,
|
||
Rseries: Rseries,
|
||
Rparallel: Rparallel,
|
||
drawArrow: drawArrow,
|
||
fieldLinesPointCharge: fieldLinesPointCharge,
|
||
chargeMark: chargeMark,
|
||
magneticFieldGrid: magneticFieldGrid,
|
||
molecule: molecule,
|
||
createGasSim: createGasSim,
|
||
batteryEMF: batteryEMF,
|
||
resistor: resistor,
|
||
capacitorSymbol: capacitorSymbol,
|
||
ammeterSymbol: ammeterSymbol,
|
||
voltmeterSymbol: voltmeterSymbol,
|
||
lightbulbSymbol: lightbulbSymbol,
|
||
inductorSymbol: inductorSymbol,
|
||
wire: wire,
|
||
CONST: CONST,
|
||
celsiusToKelvin: celsiusToKelvin,
|
||
kelvinToCelsius: kelvinToCelsius,
|
||
atmToPa: atmToPa,
|
||
paToAtm: paToAtm,
|
||
litersToM3: litersToM3,
|
||
m3ToLiters: m3ToLiters
|
||
};
|
||
})();
|