| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- /**
- * T21 地面/管线视图条件 纯逻辑检查。
- *
- * 决策地图票 05 锁死了三条硬约束,这里逐条断言:
- * ① 管线视图压低相机 + 加大 pitch,且 pitch 上限放宽到允许平视;
- * ② **切换视图不重置旋转中心**(`viewHprFor()` 的签名里根本没有中心参数,从构造上杜绝);
- * ③ 三键分工不变 —— 管线视图只换 limits,不新增输入绑定(契约层面由 t03 覆盖,这里断言 limits 取值)。
- * 另外覆盖:地面视图还原(用进管线前记下的倾角/距离)、图层透明度、配置覆盖与脏值兜底。
- *
- * 运行:node .scratch/cockpit-shell-panel-layout/verify/t21-view-modes.mjs
- */
- import assert from 'node:assert/strict'
- import { PITCH_LIMITS } from '../../../src/modules/cockpit/domain/camera.js'
- import {
- DEFAULT_VIEW_MODE,
- GROUND_ALPHA,
- GROUND_PITCH,
- PIPELINE_ALPHA,
- PIPELINE_CAMERA,
- VIEW_MODES,
- isKnownViewMode,
- planViewMode,
- viewHprFor,
- } from '../../../src/modules/cockpit/domain/viewModes.js'
- // ---------------------------------------------------------------- 1. 词汇
- assert.equal(DEFAULT_VIEW_MODE, VIEW_MODES.GROUND, '默认是地面视图')
- assert.equal(isKnownViewMode('ground'), true, 'ground 合法')
- assert.equal(isKnownViewMode('pipeline'), true, 'pipeline 合法')
- assert.equal(isKnownViewMode('管线'), false, '中文不是合法键(脏值要挡住)')
- // ---------------------------------------------------------------- 2. 地面视图
- const ground = planViewMode(VIEW_MODES.GROUND)
- assert.equal(ground.mode, VIEW_MODES.GROUND, '地面视图模式')
- assert.deepEqual(ground.limits, PITCH_LIMITS.ground, '地面视图用常规俯视上下限')
- assert.equal(ground.terrainAlpha, 1, '地面视图地形不透明(还原)')
- assert.equal(ground.tilesetAlpha, 1, '地面视图模型不透明(还原)')
- assert.equal(ground.rangeFactor, 1, '地面视图不改距离')
- // ---------------------------------------------------------------- 3. 管线视图
- const pipeline = planViewMode(VIEW_MODES.PIPELINE)
- assert.equal(pipeline.mode, VIEW_MODES.PIPELINE, '管线视图模式')
- assert.deepEqual(pipeline.limits, PITCH_LIMITS.pipeline, '管线视图放宽 pitch 上限(允许平视/轻微仰视)')
- assert.ok(pipeline.limits.max > PITCH_LIMITS.ground.max, '管线视图的 pitch 上限必须比地面视图更宽')
- assert.equal(pipeline.pitch, PIPELINE_CAMERA.pitch, '管线视图用预设倾角')
- assert.ok(pipeline.pitch > GROUND_PITCH, '管线视图的 pitch 要更"平"(比地面视图大)')
- assert.ok(pipeline.rangeFactor < 1, '管线视图要压低弹簧臂(距离变小)')
- assert.equal(pipeline.terrainAlpha, PIPELINE_ALPHA.terrain, '管线视图地形半透明')
- assert.equal(pipeline.tilesetAlpha, PIPELINE_ALPHA.tileset, '管线视图模型半透明')
- assert.ok(pipeline.terrainAlpha > 0 && pipeline.terrainAlpha < 1, '半透明而非完全隐藏(保留空间参照)')
- assert.ok(pipeline.tilesetAlpha > 0 && pipeline.tilesetAlpha < 1, '模型也是半透明而非隐藏')
- // 未知模式退回地面视图,不抛错
- assert.equal(planViewMode('nope').mode, VIEW_MODES.GROUND, '未知模式应退回地面视图')
- // 配置覆盖:只认有限数字
- const overridden = planViewMode(VIEW_MODES.PIPELINE, { pitch: -30, rangeFactor: 0.5, terrainAlpha: 0.7, tilesetAlpha: 0.6 })
- assert.equal(overridden.pitch, -30, '倾角可被配置覆盖')
- assert.equal(overridden.rangeFactor, 0.5, '距离系数可被配置覆盖')
- assert.equal(overridden.terrainAlpha, 0.7, '地形透明度可被配置覆盖')
- assert.equal(overridden.tilesetAlpha, 0.6, '模型透明度可被配置覆盖')
- const dirty = planViewMode(VIEW_MODES.PIPELINE, { pitch: 'x', rangeFactor: NaN, terrainAlpha: null })
- assert.equal(dirty.pitch, PIPELINE_CAMERA.pitch, '脏倾角应退回预设')
- assert.equal(dirty.rangeFactor, PIPELINE_CAMERA.rangeFactor, '脏系数应退回预设')
- assert.equal(dirty.terrainAlpha, PIPELINE_ALPHA.terrain, '脏透明度应退回预设')
- // ---------------------------------------------------------------- 4. 切换时的相机变换(不重置中心)
- const current = { heading: 42, pitch: -55, range: 12000 }
- const toPipeline = viewHprFor(current, pipeline, null)
- assert.equal(toPipeline.heading, 42, 'heading 原样带走(切换视图不转向)')
- assert.equal(toPipeline.pitch, PIPELINE_CAMERA.pitch, '倾角按管线预设')
- assert.equal(toPipeline.range, 12000 * PIPELINE_CAMERA.rangeFactor, '距离按系数压低')
- // 关键:返回值里没有 pivot/中心字段 —— 从构造上就不可能重置中心
- assert.equal('pivot' in toPipeline, false, '切换视图不得产出中心字段(不重置中心的构造性保证)')
- assert.equal('pivot' in pipeline, false, '视图计划里也不该有中心')
- // 管线视图的距离被夹在合理区间(贴着地面看,但不至于钻进地表)
- const tooFar = viewHprFor({ heading: 0, pitch: -55, range: 500000 }, pipeline, null)
- assert.equal(tooFar.range, PIPELINE_CAMERA.maxRange, '超远的距离应被夹到管线视图上限')
- const tooNear = viewHprFor({ heading: 0, pitch: -55, range: 200 }, pipeline, null)
- assert.equal(tooNear.range, PIPELINE_CAMERA.minRange, '过近的距离应被夹到管线视图下限')
- // 回地面:用进管线前记下的倾角/距离还原(决策地图「地面视图还原」)
- const baseline = { pitch: -62, range: 9000 }
- const backToGround = viewHprFor({ heading: 42, pitch: -18, range: 3000 }, ground, baseline)
- assert.equal(backToGround.heading, 42, '回地面也不改朝向')
- assert.equal(backToGround.pitch, -62, '回地面应还原进管线前的倾角')
- assert.equal(backToGround.range, 9000, '回地面应还原进管线前的距离')
- // 没有基线时用默认地面倾角兜底(比如直接以管线视角进系统)
- const groundNoBaseline = viewHprFor({ heading: 0, pitch: -18, range: 3000 }, ground, null)
- assert.equal(groundNoBaseline.pitch, GROUND_PITCH, '没有基线时回落到默认地面倾角')
- // 中心不变是行为契约:同一次切换,heading 与中心都不在变换范围内
- const stateBefore = { pivot: { lon: 110.39, lat: 28.44, height: 111.6 }, hpr: { heading: 90, pitch: -55, range: 8000 } }
- const afterSwitch = { ...stateBefore, hpr: { ...stateBefore.hpr, ...viewHprFor(stateBefore.hpr, pipeline, null) } }
- assert.deepEqual(afterSwitch.pivot, stateBefore.pivot, '切换视图后中心必须一模一样')
- console.log(
- `T21 视图条件检查通过:地面/管线计划(管线 pitch ${PIPELINE_CAMERA.pitch}° / 距离 ×${PIPELINE_CAMERA.rangeFactor} / 地形 α${PIPELINE_ALPHA.terrain} / 模型 α${PIPELINE_ALPHA.tileset})、pitch 上限放宽到 ${PITCH_LIMITS.pipeline.max}°、切换不产中心字段(构造性不重置)、地面视图按基线还原、脏配置兜底。`,
- )
|