Files
2026-05-27 23:29:53 +01:00

4.0 KiB

OpenSpec Change: enterprise-gaming-ik-solver

  • Change ID: enterprise-gaming-ik-solver
  • Package: @suaveplan/ik-solver
  • Category: gaming/rigs
  • Target path: gaming/rigs/ik-solver
  • Status: PROPOSED
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/ik-solver.md

Summary

Deliver @suaveplan/ik-solver on the genesis branch from scratch at enterprise quality. The package provides pure-math inverse kinematics solvers: two-bone analytic IK (arm/leg chains), look-at (head/eye), and multi-chain FABRIK for arbitrary-length chains. No Three.js dependency — callers pass Vec3/Quat structs and receive solved bone rotations. Genesis adds configurable iteration limits, convergence thresholds, Zod chain config schemas, structured errors, OTel solve-time histogram, and 100% coverage with property tests for geometric correctness.


Current-state audit (genesis)

Package does not exist on genesis yet — fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) IkSolverError; IK.* codes
@suaveplan/types (workspace:^) Shared primitives
zod (catalog:forms) Chain config schemas
@opentelemetry/api (catalog:observability) Solve duration histogram, iteration count gauge

Public API surface

// Two-bone analytic IK (arm/leg)
function solveTwoBone(chain: TwoBoneChain, target: Vec3, hint?: Vec3): TwoBoneSolution

interface TwoBoneChain {
  root: Vec3
  mid: Vec3
  tip: Vec3
  upperLength: number
  lowerLength: number
}

interface TwoBoneSolution {
  upperRotation: Quat
  lowerRotation: Quat
  reached: boolean
}

// Look-at IK
function solveLookAt(
  bonePosition: Vec3,
  boneForward: Vec3,
  boneUp: Vec3,
  target: Vec3,
  clampAngle?: number    // max angle in radians
): Quat

// FABRIK multi-chain
function solveFabrik(chain: FabrikChain, options?: FabrikOptions): FabrikSolution

interface FabrikChain {
  joints: Vec3[]
  lengths: number[]
  target: Vec3
  root?: Vec3           // if set, root is constrained to original position
}

interface FabrikOptions {
  maxIterations?: number      // default 10
  tolerance?: number          // convergence threshold, default 0.001
  constraints?: JointConstraint[]
}

interface JointConstraint {
  jointIndex: number
  minAngle: number
  maxAngle: number
  axis: Vec3
}

interface FabrikSolution {
  joints: Vec3[]
  rotations: Quat[]
  iterations: number
  converged: boolean
}

// Utility: build chain from bone descriptors
function chainFromBones(bones: BoneDescriptor[], indices: number[]): FabrikChain

Error codes

type IkErrorCode =
  | 'IK_CHAIN_TOO_SHORT'
  | 'IK_DEGENERATE_CHAIN'
  | 'IK_MAX_ITERATIONS_EXCEEDED'
  | 'IK_INVALID_CONSTRAINT'

New additions for genesis

  1. Hint vector for two-bone — optional pole-vector hint controls elbow/knee direction.
  2. Joint constraints in FABRIK — hinge and cone constraints applied per iteration.
  3. OTel — ik.solve_duration_ms histogram; ik.iterations histogram.
  4. Property tests — two-bone: tip distance from target ≤ tolerance after solve; look-at: resulting forward aligns with target direction.

File layout

gaming/rigs/ik-solver/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    constants/index.ts
    types/common.ts
    types/common.md
    solvers/
      two-bone/
        two-bone.ts
        two-bone.md
        two-bone.types.ts
        two-bone.test.ts
        index.ts
      look-at/
        look-at.ts
        look-at.md
        look-at.test.ts
        index.ts
      fabrik/
        fabrik.ts
        fabrik.md
        fabrik.types.ts
        fabrik.test.ts
        index.ts
    utils/
      vec3-math.ts
      vec3-math.md
      vec3-math.test.ts
  tests/
    property.test.ts

Impact

Downstream: @suaveplan/r3f-animation uses these solvers for foot IK and hand IK; @suaveplan/animation-events uses look-at for gaze control. Prerequisites: @suaveplan/error, @suaveplan/types.