// geom.js — 纯渲染几何(接收数字、返回写入值,零 Pixi、零求值)。 // // 这里只放「把 core 已算好的 SlotTransform / facing / worldY 翻译成 Sprite 写入值」时 // 需要的纯几何换算:facing 翻转符号、头部扭头反射轴镜像、纵深透视缩放与 zIndex。 // // 红线:本文件**绝不**出现矩阵相乘 / 关键帧采样 / 镜头补间——那些是 @drama/core 的活 // (render-pixi §5.1)。core 已把世界矩阵 decompose 成 SlotTransform,这里只摆放。 // 镜像反射轴这类「依赖具体贴图锚点的纯渲染几何」按 render-pixi §3 留在本包。 const DEG = Math.PI / 180 /** * 贴图基准朝向约定(移植自原型 character-actor.js `_applyRootScale`): * 本套角色贴图基准朝向 = 屏幕左;故 facing='left' 不翻转,facing='right' 才水平翻转。 * 返回根容器水平缩放符号(±1),调用方乘到 `root.scale.x` 上。 * @param {'left'|'right'} facing * @returns {number} -1(翻转)或 1(不翻转) */ export function bodyFlipSign(facing) { return facing === 'right' ? -1 : 1 } /** * 头部独立扭头镜像符号(移植自原型 `update` 的 headMirror): * headFacing 与 facing 不同 → 头 + 脸水平翻转(看向另一边),返回 -1;否则 1。 * @param {'left'|'right'} facing * @param {('left'|'right'|null)=} headFacing * @returns {number} -1 或 1 */ export function headMirrorSign(facing, headFacing) { return headFacing && headFacing !== facing ? -1 : 1 } /** * 头/脸部件在扭头镜像下的位置反射(移植自原型 `px = 2*headBoneX - d.x`): * 翻转的反射轴 = 头骨节点 x;脸的偏移也要绕它镜像才不错位。 * 仅当 headMirror===-1 时反射;否则原样返回。 * @param {number} x 部件原始 x(来自 SlotTransform) * @param {number} headBoneX 头骨世界 x(反射轴) * @param {number} headMirror -1 或 1 * @returns {number} */ export function reflectHeadX(x, headBoneX, headMirror) { return headMirror === -1 ? 2 * headBoneX - x : x } /** * 把一个 SlotTransform 解出最终要写进 Sprite 的属性值(纯数据→纯数据)。 * mirror=true 触发水平翻转(scale.x *= -1);可选叠加头部扭头镜像(slot 为 head/face 时)。 * @param {import('@drama/core').SlotTransform} t * @param {object} [mods] * @param {number=} mods.headMirror -1/1,仅对 head/face slot 传入 * @param {number=} mods.headBoneX 反射轴 x,仅 headMirror=-1 时用 * @param {boolean=} mods.isHeadSlot 该 slot 是否参与扭头镜像(head/face) * @returns {{x:number,y:number,rotation:number,scaleX:number,scaleY:number}} */ export function resolveSlotWrite(t, mods = {}) { const headMirror = mods.isHeadSlot ? (mods.headMirror || 1) : 1 let scaleX = t.scaleX if (t.mirror) scaleX = -scaleX scaleX *= headMirror let x = t.x if (mods.isHeadSlot && headMirror === -1) { x = reflectHeadX(t.x, mods.headBoneX || 0, headMirror) } return { x, y: t.y, rotation: t.rotation, scaleX, scaleY: t.scaleY } } /** * 纵深透视缩放(移植自原型 `effScale` 的 depthScale 部分): * 线性插值:y=nearY(近/下)→ nearScale,y=farY(远/上)→ farScale,区间外夹紧。 * 返回缩放倍数(相对 baseScale 的乘数)。无 depth 配置时调用方应跳过(恒 1)。 * @param {number} worldY * @param {{nearY:number,farY:number,nearScale:number,farScale:number}} depth * @returns {number} */ export function perspectiveScale(worldY, depth) { const { nearY, farY, nearScale, farScale } = depth const t = (worldY - farY) / (nearY - farY || 1) const clamped = Math.max(0, Math.min(1, t)) return farScale + (nearScale - farScale) * clamped } /** * 纵深遮挡排序键(移植自原型 `zIndex = Math.round(worldY)`): * 越靠下(worldY 越大=越近)越靠前。遮挡片用 baseY 同尺度,人物 worldY 小于 baseY 被遮。 * @param {number} worldY * @returns {number} */ export function depthZIndex(worldY) { return Math.round(worldY) } export { DEG }