Appearance
经纬的统一
背景:球面坐标(方位角/仰角/距离)在模型、道具、光照三处各自实现,公式微妙差异积累成 bug。 过程:core/orbit.ts 抽出 orbitToCartesian/cartesianToOrbit 单一真相;ModelInstance/PropInstance 增 positionMode+orbit 字段;序列化与变换面板统一接入。
序、三本经书
王逸发现一个怪事:同一个模型,在模型面板里输入的方位角,和道具面板里输入的方位角,转到世界坐标后差了一丝。
一丝,足以让镜头在渲染时偏出画面边缘。
追下去,真相令人疲惫:球面坐标(方位角 azimuth、仰角 elevation、距离 distance)在三个地方各写了一套——模型管理、道具系统、光照推导。三本"经书",抄自同一源头,却各自带了抄写员的笔误。
atan2(x, z) 还是 atan2(z, x)?asin(y/len) 还是 asin(len/y)?每个子系统都"差不多对",合在一起就"处处不对"。
一、唯一的经书
解法是把三本经书烧掉,重抄一本:core/orbit.ts。
orbitToCartesian 和 cartesianToOrbit 成了球面坐标唯一的翻译官。公式钉死——对齐 lighting.ts 的推导(atan2(x, z) / asin(y / len)),谁要用,来这里取,不许自己重写。
core/types.ts 里,ModelInstance 和 PropInstance 长出了新器官:positionMode('cartesian' | 'orbit')+ orbitAzimuth / orbitElevation / orbitDistance。可选,向后兼容——老存档没有这些字段,模型照常站在原处。
二、议会下发诏书
新器官要有管家。model-manager.ts 添了 setOrbit / getOrbit / setPositionMode / getPositionMode;model-ops.ts 包了一层薄封装(setModelOrbit 等),让调用方不必直戳 manager 内部。props.ts 也照做——道具从此和模型说同一种方位角方言。
序列化是另一道关。scene-serialize.ts 在模型与道具的存档里加 orbit 字段;还原时,若 positionMode === 'orbit',优先 setOrbit 落位——保证"存的是方位角、读的也是方位角",不丢精度。
三、面板的手感
前线是 resource-detail-helpers.ts 的 buildTransformCard。
它改用 render() 闭包 + addModeRow 做坐标模式切换。用户选"轨道模式",滑条即时从 X/Y/Z 换成 方位角/仰角/距离;选"笛卡尔模式",滑条切回 X/Y/Z。actor、stage、prop 三种目标,一套 UI 统一覆盖。
TransformField 这个旧接口被请退了——它的存在只是为了在两种坐标系间反复横跳,现在不需要了。
统一的代价,是先承认自己抄错过经书。三套实现合并成一套时,那两处"差不多对"的笔误,终于无处藏身。
四、王的度量
验证:tsc --noEmit 零错,vite build 成功(481 模块,2.03s),vitest 1061 通过仅余 2 失败(与 orbit 无关,是既有遗留)。
ADR-049 状态从「已提议」改为「已实现」。
王逸在模型面板和道具面板各输了一组相同的方位角。这一次,两个镜头,同一个世界。
聚合的成熟,不是功能越加越多,是把三本各说各话的经书,合成一本所有人跪拜的。
教训:坐标系公式只应存在一处单一真相,多处复制必生漂移。