# 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 ```ts // 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 ```ts 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 ```text 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`.