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

84 lines
3.9 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.
/**
* 调试:retro_pinball 拱形候选的力度扫描 + 精确参数输出
* 运行:node dev_test_scripts/debug/debug_retro_pinball_arch_sweep.js
*/
'use strict'
const fs = require('fs')
const path = require('path')
const vm = require('vm')
const HTML_PATH = path.join(__dirname, '..', '..', 'public', 'tools', 'thought_lab', 'labs', 'retro_pinball', 'index.html')
const html = fs.readFileSync(HTML_PATH, 'utf-8')
const grab = (a, b) => {
const s = html.indexOf(a), e = html.indexOf(b)
if (s < 0 || e < 0 || e <= s) throw new Error('未找到标记块: ' + a)
return html.slice(s, e)
}
const code = grab('/* ==PHYSICS_CORE_START== */', '/* ==PHYSICS_CORE_END== */') + '\n' + grab('/* ==TABLE_GEOM_START== */', '/* ==TABLE_GEOM_END== */')
const sandbox = { Math, console }
vm.createContext(sandbox)
vm.runInContext(code, sandbox)
const PC = sandbox.PhysicsCore
const { BALL_R, G_PX, SPRING_X, SPRING_TOP, LANE_X, buildTableStatics, slotIndexAt, SLOT_X0, SLOT_X1 } = sandbox
const DT = 1 / 360
function params() {
return { g: G_PX * Math.sin(14 * Math.PI / 180), e: 0.55, mu: 0.12, airK: 0.02, rollR: 0.25, restV: 50, iterations: 8, slop: 0.3, correction: 0.8, maxV: 4200 };
}
function fitCircle(p1, p2, p3) {
const [x1, y1] = p1, [x2, y2] = p2, [x3, y3] = p3
const d = 2 * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2))
const ux = ((x1 * x1 + y1 * y1) * (y2 - y3) + (x2 * x2 + y2 * y2) * (y3 - y1) + (x3 * x3 + y3 * y3) * (y1 - y2)) / d
const uy = ((x1 * x1 + y1 * y1) * (x3 - x2) + (x2 * x2 + y2 * y2) * (x1 - x3) + (x3 * x3 + y3 * y3) * (x2 - x1)) / d
return { cx: ux, cy: uy, r: Math.hypot(x1 - ux, y1 - uy) }
}
const deg = a => a * 180 / Math.PI
function makeArch(leftExit, top, rightEnd) {
const c = fitCircle(leftExit, top, rightEnd)
let aR = deg(Math.atan2(rightEnd[1] - c.cy, rightEnd[0] - c.cx))
let aL = deg(Math.atan2(leftExit[1] - c.cy, leftExit[0] - c.cx))
while (aL > aR) aL -= 360
return { cx: c.cx, cy: c.cy, r: c.r, a0: aL, a1: aR }
}
function simulate(arch, v0, seedVx) {
sandbox.ARCH = arch
const table = buildTableStatics(5, 1)
const b = PC.createBall(SPRING_X, SPRING_TOP - BALL_R, BALL_R)
b.vy = -v0; b.vx = seedVx || 0
let minY = 1e9, enteredLeft = false, nan = false
for (let t = 0; t < 10; t += DT) {
PC.stepWorld([b], table.statics, params(), DT)
minY = Math.min(minY, b.y)
if (b.x < 90 && b.y > 200 && b.y < 450) enteredLeft = true
if (!isFinite(b.x) || !isFinite(b.y)) { nan = true; break }
}
const sp = Math.hypot(b.vx, b.vy)
return { minY, enteredLeft, nan, x: b.x, y: b.y, sp, inField: b.x >= SLOT_X0 && b.x <= SLOT_X1, slot: slotIndexAt(b.x, b.y), fellBackLane: b.x > LANE_X };
}
// 聚焦最优候选 C4,输出精确值
const CHOSEN = { leftExit: [60, 340], top: [240, 50], rightEnd: [442, 220] }
const arch = makeArch(CHOSEN.leftExit, CHOSEN.top, CHOSEN.rightEnd)
console.log('选中拱形精确参数:')
console.log(` cx=${arch.cx.toFixed(3)} cy=${arch.cy.toFixed(3)} r=${arch.r.toFixed(3)} a0=${arch.a0.toFixed(3)} a1=${arch.a1.toFixed(3)}`)
console.log(` 左出口=(${CHOSEN.leftExit}) 弧顶=(${CHOSEN.top}) 右端=(${CHOSEN.rightEnd})`)
console.log('')
console.log('力度扫描(seedVx=5):')
const powers = [900, 1200, 1500, 1800, 2100, 2400, 2700, 2900, 3200]
for (const v0 of powers) {
const r = simulate(arch, v0, 5)
const tag = r.nan ? 'NaN!' : r.fellBackLane ? '落回发射道' : r.enteredLeft ? '绕弧左侧落入' : '其他'
console.log(` v0=${v0} minY=${r.minY.toFixed(0)} ${tag} final=(${r.x.toFixed(0)},${r.y.toFixed(0)}) sp=${r.sp.toFixed(0)} slot=${r.slot}`)
}
console.log('')
console.log('满力微扰扫描(v0=2900,落点分布):')
const slots = {}
for (let sv = -8; sv <= 8; sv += 2) {
const r = simulate(arch, 2900, sv)
slots[r.slot] = (slots[r.slot] || 0) + 1
console.log(` seedVx=${sv} 落点=(${r.x.toFixed(0)},${r.y.toFixed(0)}) slot=${r.slot} enteredLeft=${r.enteredLeft}`)
}
console.log(' 落坑分布:', JSON.stringify(slots))