t03-antimeridian-walkthrough.mjs 4.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. /**
  2. * 跨 ±180 经线真机复验:维护者反馈「到达经度 0 的区域时会抽搐后旋转一周到达另一边」。
  3. * 在真机上把中心移到 179.x,再连续东西向拖拽跨过 180,观察经度是否绕远路。
  4. *
  5. * 运行(需先起 `yarn dev`):node .scratch/cockpit-shell-panel-layout/verify/t03-antimeridian-walkthrough.mjs
  6. */
  7. import { join } from 'node:path'
  8. import { tmpdir } from 'node:os'
  9. const BASE = process.env.COCKPIT_BASE || 'http://localhost:8000'
  10. const CHROME = process.env.CHROME_PATH || 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'
  11. const PUPPETEER = 'file:///C:/Users/ASUS/.dsh/profiles/desktop/node_modules/puppeteer-core/lib/puppeteer/puppeteer-core.js'
  12. const puppeteer = (await import(PUPPETEER)).default
  13. const browser = await puppeteer.launch({
  14. executablePath: CHROME,
  15. headless: true,
  16. userDataDir: join(tmpdir(), 'cockpit-t03-am-profile'),
  17. defaultViewport: { width: 1680, height: 950 },
  18. args: ['--no-sandbox', '--disable-dev-shm-usage', '--no-first-run'],
  19. })
  20. const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
  21. const shortest = (a, b) => ((b - a + 540) % 360) - 180
  22. try {
  23. const page = await browser.newPage()
  24. const errors = []
  25. page.on('pageerror', (e) => errors.push(String((e && e.message) || e)))
  26. await page.goto(`${BASE}/#/cockpit`, { waitUntil: 'networkidle2', timeout: 60000 })
  27. await page.waitForSelector('.cockpit-shell', { timeout: 20000 })
  28. await page.waitForFunction(() => !!window.__cockpitMap, { timeout: 30000 })
  29. await page
  30. .waitForFunction(() => document.querySelector('.cockpit-shell__map .map-canvas')?.dataset.mapEngine === 'ready', {
  31. timeout: 60000,
  32. })
  33. .catch(() => {})
  34. await sleep(1000)
  35. const read = () =>
  36. page.evaluate(() => {
  37. const s = window.__cockpitMap.getCameraState()
  38. return { pivot: s.pivot, shown: s.shownPivot, hpr: s.hpr, settled: s.settled }
  39. })
  40. // 让相机离地面近一点,跨经线的步长才不至于一次跳过大半圈。
  41. await page.evaluate(() => window.__cockpitMap.setCameraDamping(110))
  42. await page.evaluate(() => window.__cockpitMap.setCameraFocus({ lon: 179.0, lat: 10 }))
  43. await page.waitForFunction(() => window.__cockpitMap.getCameraState().settled === true, { timeout: 5000 }).catch(() => {})
  44. await sleep(600)
  45. const before = await read()
  46. console.log('起点:', JSON.stringify({ pivot: before.pivot, r: Math.round(before.hpr.range) }))
  47. const canvasCenter = await page.evaluate(() => {
  48. const b = document.querySelector('.cockpit-shell__map .map-canvas').getBoundingClientRect()
  49. return { x: b.x + b.width / 2, y: b.y + b.height / 2 }
  50. })
  51. // 连续向右拖(抓着地图拖 → 中心西移 → 经度增大,会跨过 ±180)。
  52. const lons = []
  53. await page.mouse.move(canvasCenter.x, canvasCenter.y)
  54. await page.mouse.down({ button: 'left' })
  55. for (let i = 1; i <= 10; i += 1) {
  56. await page.mouse.move(canvasCenter.x - i * 60, canvasCenter.y, { steps: 3 })
  57. await sleep(60)
  58. const s = await read()
  59. lons.push(s.pivot.lon)
  60. }
  61. await page.mouse.up({ button: 'left' })
  62. await sleep(1200)
  63. // 解缠:从**起点**开始按最短弧累加,得出真实走过的经度总量。
  64. // 关键判据:若跨 ±180 走了长边,单步增量会接近 ±360 的整数倍,且方向会翻转。
  65. const track = [before.pivot.lon, ...lons]
  66. let acc = 0
  67. const arc = [0]
  68. for (let i = 1; i < track.length; i += 1) {
  69. acc += shortest(track[i - 1], track[i])
  70. arc.push(acc)
  71. }
  72. console.log('落点经度:', lons.map((v) => v.toFixed(3)).join(' → '))
  73. console.log('弧长累加:', arc.map((v) => v.toFixed(3)).join(' → '))
  74. console.log('总弧长 °:', arc[arc.length - 1].toFixed(3), '(应与拖拽量级相符,不是 ±360 的整数倍)')
  75. console.log('单调推进:', arc.every((v, i) => i === 0 || v >= arc[i - 1] - 1e-9))
  76. const overshoot = Math.abs(acc - shortest(track[0], track[track.length - 1]))
  77. console.log('与首尾最短弧之差 °:', overshoot.toFixed(3), '(≈0 说明全程没绕远路)')
  78. console.log('终点:', JSON.stringify(await read()))
  79. console.log('页面报错:', errors.length ? errors.slice(0, 3).join(' | ') : '(无)')
  80. } finally {
  81. await browser.close()
  82. }