Files
Toolbox/dev_test_scripts/unit/test_three_body_integrator.js
yangxiangyuan 4492b4106a feat: 新增3个思想实验室项目,完善私有剪贴板授权与上传功能,添加配套测试脚本
1. 新增怀旧弹珠台、人性与投资、穿越三体、四维空间四个思想实验室项目
2. 为私有剪贴板添加业务层授权校验,优化上传进度显示与鉴权失败处理
3. 新增多个调试与单元测试脚本,覆盖三体积分、弹珠台物理、集成测试等场景
4. 补全穿越三体项目的完整HTML页面资源
2026-08-07 17:49:24 +08:00

54 lines
2.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// test_three_body_integrator.js - 穿越三体 · 三体积分器校验
// 从 public/tools/thought_lab/labs/three_body_journey/index.html 抽取
// THREEBODY_MATH_BEGIN/END 之间的纯物理代码执行校验(测试对象 = 上线代码)。
// 校验:RK4 积分动量守恒、能量漂移极小、加速度满足牛顿第三定律对称性。
// 运行:node dev_test_scripts/unit/test_three_body_integrator.js
// ============================================================
'use strict'
const fs = require('fs')
const path = require('path')
const vm = require('vm')
const HTML = path.resolve(__dirname, '..', '..', 'public', 'tools', 'thought_lab', 'labs', 'three_body_journey', 'index.html')
const src = fs.readFileSync(HTML, 'utf8')
const m = src.match(/\/\/ ==== THREEBODY_MATH_BEGIN ====\n([\s\S]*?)\/\/ ==== THREEBODY_MATH_END ====/)
if (!m) { console.error('FAIL - 未找到 THREEBODY_MATH 标记块'); process.exit(1) }
const api = vm.runInNewContext(m[1] + ';({ tbAccel, tbStep, tbEnergy, tbMomentum })')
let pass = 0, fail = 0
const check = (name, cond) => {
if (cond) { pass++; console.log('PASS -', name) }
else { fail++; console.log('FAIL -', name) }
}
const masses = [1, 1, 1]
const eps2 = 0.02
let pos = [1, 0, -0.5, Math.sqrt(3) / 2, -0.5, -Math.sqrt(3) / 2]
let vel = [0, 0.42, -0.38, -0.2, 0.38, -0.22]
// 牛顿第三定律:Σ mᵢaᵢ = 0(作用力反作用力,任意位形)
{
const ms = [1, 2, 0.5]
const a = api.tbAccel([0, 0, 1, 0.3, -0.7, 1.2], ms, 0)
let sx = 0, sy = 0
for (let i = 0; i < 3; i++) { sx += ms[i] * a[2 * i]; sy += ms[i] * a[2 * i + 1] }
check('Σ mᵢaᵢ = 0(作用反作用)', Math.abs(sx) < 1e-12 && Math.abs(sy) < 1e-12)
}
// 动量守恒:跑 5000 步
const p0 = api.tbMomentum(vel, masses)
const E0 = api.tbEnergy(pos, vel, masses, eps2)
for (let i = 0; i < 5000; i++) [pos, vel] = api.tbStep(pos, vel, masses, 0.002, eps2)
const p1 = api.tbMomentum(vel, masses)
const E1 = api.tbEnergy(pos, vel, masses, eps2)
check('动量守恒(5000 步,|Δp|<1e-9)', Math.abs(p1[0] - p0[0]) < 1e-9 && Math.abs(p1[1] - p0[1]) < 1e-9)
check(`能量漂移小(|ΔE/E0|<1e-3,实际 ${Math.abs((E1 - E0) / E0).toExponential(2)})`, Math.abs((E1 - E0) / E0) < 1e-3)
// 有限性:5000 步后位置/速度均为有限数(软化避免奇点崩溃)
check('积分稳定(全部有限)', [...pos, ...vel].every(Number.isFinite))
console.log(fail === 0 ? `ALL PASS (${pass})` : `FAILED: ${fail} / ${pass + fail}`)
process.exit(fail === 0 ? 0 : 1)