index.js 3.38 KB
// index.js — @drama/rig 唯一公共面(§3 契约)。
//
// 职责:汇总 rigs/clips/poses 三类纯数据,做「命名姿势覆盖同名内置 clip」的合并,深冻结,
// 暴露查询函数与轨道元数据。**无任何求值 / 插值 / 渲染逻辑**(那些全在 @drama/core)。
//
// 不可变性约定:采用「方案 A —— 返回深冻结(Object.freeze 递归)的共享常量」。
//   零拷贝、调用方误改即抛(严格模式)/ 静默失败(非严格)。
//   core 侧约定不修改 rig 返回的数据(增量算在 core 内部临时结构里),故方案 A 足够。

// 形状契约 typedef 经公共面再导出,供下游 `import('@drama/rig').RigDefinition` 等消费
// (@drama/core / @drama/render-pixi 基于此对齐,详见 types.js)。
/**
 * @typedef {import('./types.js').RigDefinition} RigDefinition
 * @typedef {import('./types.js').Bone} Bone
 * @typedef {import('./types.js').Slot} Slot
 * @typedef {import('./types.js').AnimationClip} AnimationClip
 * @typedef {import('./types.js').Keyframe} Keyframe
 * @typedef {import('./types.js').NamedPose} NamedPose
 */

import { RIGS } from './rigs/index.js'
import { CLIPS, TRACKS as _TRACKS, TRACK_BONES as _TRACK_BONES, TRACK_DEFAULTS as _TRACK_DEFAULTS } from './clips/index.js'
import { POSES } from './poses.js'

/**
 * 递归深冻结一个普通对象/数组(就地冻结,纯数据无循环引用)。
 * @template {unknown} T
 * @param {T} obj
 * @returns {T}
 */
function deepFreeze(obj) {
  if (obj === null || typeof obj !== 'object' || Object.isFrozen(obj)) return obj
  for (const value of Object.values(/** @type {Record<string, unknown>} */ (obj))) {
    deepFreeze(value)
  }
  return Object.freeze(obj)
}

// —— 合并:命名姿势覆盖同名内置 clip(验收 §2.4:调用方拿到合并后结果,无需自己 merge)——
/** @type {Record<string, import('./types.js').AnimationClip>} */
const MERGED_CLIPS = deepFreeze({ ...CLIPS, ...POSES })

/** @type {Record<string, import('./types.js').NamedPose>} */
const FROZEN_POSES = deepFreeze({ ...POSES })

/** @type {Record<string, import('./types.js').RigDefinition>} */
const FROZEN_RIGS = deepFreeze({ ...RIGS })

/** 有序轨道:['base','lower_body','arm_l','arm_r','head']。 @type {string[]} */
export const TRACKS = deepFreeze([..._TRACKS])

/** 各 track 拥有的 bone 子集(互不相交)。 @type {Record<string, string[]>} */
export const TRACK_BONES = deepFreeze({ ..._TRACK_BONES })

/** 无指定动作时回落的默认 clip id。 @type {Record<string, string>} */
export const TRACK_DEFAULTS = deepFreeze({ ..._TRACK_DEFAULTS })

/**
 * 取骨架定义。未知 id 返回 null(不抛)。
 * @param {string} id
 * @returns {import('./types.js').RigDefinition|null}
 */
export function getRig(id) {
  return FROZEN_RIGS[id] ?? null
}

/**
 * 取内置动作库全集(含命名姿势合并后结果)。
 * @returns {Record<string, import('./types.js').AnimationClip>}
 */
export function getClips() {
  return MERGED_CLIPS
}

/**
 * 取命名姿势集。
 * @returns {Record<string, import('./types.js').NamedPose>}
 */
export function getPoses() {
  return FROZEN_POSES
}

/**
 * 取单个 clip(含命名姿势,姿势覆盖同名内置)。未知 id 返回 null。
 * @param {string} id
 * @returns {import('./types.js').AnimationClip|null}
 */
export function getClip(id) {
  return MERGED_CLIPS[id] ?? null
}