depth.test.js 3.14 KB
// depth.test.js — 透视缩放 + zIndex 纵深 + 遮挡判定(render-pixi §8 验收 7)。
//
// 对照原型 pixi-stage.js / character-actor.js:
//   - setActorWorldPos 后角色按 worldY 做透视缩放(近大远小)+ zIndex=round(worldY)。
//   - 遮挡片 zIndex=baseY;人物 worldY < baseY 被遮、> baseY 盖住。

import { describe, it, expect } from 'vitest'
import { perspectiveScale, depthZIndex } from '../src/geom.js'
import { isOccluded } from '../src/occluder.js'
import { createPixiRenderer } from '../src/index.js'
import { makeStubPixi, StubApplication } from './stub-pixi.js'

const depth = { nearY: 1000, farY: 200, nearScale: 1.5, farScale: 0.5 }

describe('geom 纵深纯函数', () => {
  it('perspectiveScale:近(下/大)→nearScale、远(上/小)→farScale、区间外夹紧', () => {
    expect(perspectiveScale(1000, depth)).toBeCloseTo(1.5)
    expect(perspectiveScale(200, depth)).toBeCloseTo(0.5)
    expect(perspectiveScale(600, depth)).toBeCloseTo(1.0) // 中点
    expect(perspectiveScale(2000, depth)).toBeCloseTo(1.5) // 夹紧 near
    expect(perspectiveScale(-100, depth)).toBeCloseTo(0.5) // 夹紧 far
  })

  it('perspectiveScale 单调:worldY 越大缩放越大(近大远小)', () => {
    expect(perspectiveScale(400, depth)).toBeLessThan(perspectiveScale(800, depth))
  })

  it('depthZIndex = round(worldY)', () => {
    expect(depthZIndex(456.4)).toBe(456)
    expect(depthZIndex(456.6)).toBe(457)
  })
})

describe('遮挡判定', () => {
  it('人物脚下 worldY < baseY → 被遮;> baseY → 盖住;== 不被遮', () => {
    expect(isOccluded(300, 500)).toBe(true)
    expect(isOccluded(700, 500)).toBe(false)
    expect(isOccluded(500, 500)).toBe(false)
  })
})

const rig = {
  id: 'humanoid_v1',
  canvas: { width: 1080, height: 1080 },
  bones: [{ name: 'root', parent: null, pos: { x: 0, y: 0 }, rotation: 0 }],
  slots: [{ name: 'torso', bone: 'root', zOrder: 10 }],
}

describe('setActorWorldPos 集成', () => {
  function setup() {
    const PIXI = makeStubPixi()
    const r = createPixiRenderer(new StubApplication(), { PIXI, rig })
    return { r, PIXI }
  }

  it('setActorWorldPos 写根容器位置 + zIndex=round(worldY)', () => {
    const { r } = setup()
    r.addActor('a', { rigId: 'humanoid_v1', parts: { torso: { url: 't.png' } } }, { x: 0, y: 0, facing: 'left', scale: 1 })
    r.setActorWorldPos('a', 250, 740.3)
    const actor = r._actors.get('a')
    expect(actor.root.position.x).toBe(250)
    expect(actor.root.position.y).toBe(740.3)
    expect(actor.root.zIndex).toBe(740)
  })

  it('loadScene 设 depth 后,角色按 worldY 透视缩放(baseScale=1)', async () => {
    const { r } = setup()
    r.addActor('a', { rigId: 'humanoid_v1', parts: { torso: { url: 't.png' } } }, { x: 0, y: 0, facing: 'left', scale: 1 })
    await r.loadScene({ world: { w: 2000, h: 1200 }, depth })
    r.setActorWorldPos('a', 0, 1000) // 近
    const near = Math.abs(r._actors.get('a').root.scale.x)
    r.setActorWorldPos('a', 0, 200) // 远
    const far = Math.abs(r._actors.get('a').root.scale.x)
    expect(near).toBeCloseTo(1.5)
    expect(far).toBeCloseTo(0.5)
    expect(near).toBeGreaterThan(far)
  })
})