blacktrigram 0.7.67 → 0.7.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/lib/App2.js.map +1 -1
  2. package/lib/assets/index.css +987 -987
  3. package/lib/audio/AudioAssetLoader.js.map +1 -1
  4. package/lib/audio/AudioAssetRegistry.js +2 -2
  5. package/lib/audio/AudioAssetRegistry.js.map +1 -1
  6. package/lib/audio/AudioCache.js.map +1 -1
  7. package/lib/audio/AudioManager.js +1 -1
  8. package/lib/audio/AudioManager.js.map +1 -1
  9. package/lib/audio/AudioMonitor.js.map +1 -1
  10. package/lib/audio/AudioPool.js.map +1 -1
  11. package/lib/audio/AudioProvider.js.map +1 -1
  12. package/lib/audio/AudioUtils.js.map +1 -1
  13. package/lib/audio/BoneImpactAudioMap.js.map +1 -1
  14. package/lib/components/screens/combat/CombatScreen3D.js +3 -3
  15. package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
  16. package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
  17. package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
  18. package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
  19. package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
  20. package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
  21. package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
  22. package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
  23. package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
  24. package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
  25. package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
  26. package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
  27. package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
  28. package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
  29. package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
  30. package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
  31. package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
  32. package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
  33. package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
  34. package/lib/components/screens/intro/IntroScreen3D.js +1 -1
  35. package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
  36. package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
  37. package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
  38. package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
  39. package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
  40. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
  41. package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
  42. package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
  43. package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
  44. package/lib/components/screens/training/TrainingScreen3D.js +1 -1
  45. package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
  46. package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
  47. package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
  48. package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
  49. package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
  50. package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
  51. package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
  52. package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
  53. package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
  54. package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
  55. package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
  56. package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
  57. package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
  58. package/lib/components/shared/mobile/HapticController.js.map +1 -1
  59. package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
  60. package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
  61. package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
  62. package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
  63. package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
  64. package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
  65. package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
  66. package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
  67. package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
  68. package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
  69. package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
  70. package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
  71. package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
  72. package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
  73. package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
  74. package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
  75. package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
  76. package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
  77. package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
  78. package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
  79. package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
  80. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
  81. package/lib/components/shared/ui/SplashScreen.js +2 -2
  82. package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
  83. package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
  84. package/lib/data/techniques.js.map +1 -1
  85. package/lib/hooks/useCombatTimer.js.map +1 -1
  86. package/lib/hooks/useKeyboardControls.js.map +1 -1
  87. package/lib/hooks/useSkeletalAnimation.js +2 -2
  88. package/lib/hooks/useSkeletalAnimation.js.map +1 -1
  89. package/lib/hooks/useTechniqueSelection.js.map +1 -1
  90. package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
  91. package/lib/index.js +2 -2
  92. package/lib/systems/CombatSystem.js.map +1 -1
  93. package/lib/systems/EffectCalculator.js.map +1 -1
  94. package/lib/systems/ResponsiveScaling.js.map +1 -1
  95. package/lib/systems/TrigramSystem.js.map +1 -1
  96. package/lib/systems/ai/AIPersonality.js.map +1 -1
  97. package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
  98. package/lib/systems/ai/DecisionTree.js.map +1 -1
  99. package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
  100. package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
  101. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
  102. package/lib/systems/animation/builders/SkeletonRig.js +1 -1
  103. package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
  104. package/lib/systems/animation/catalogs/AttackAnimations.js +1 -1
  105. package/lib/systems/animation/catalogs/AttackAnimations.js.map +1 -1
  106. package/lib/systems/animation/catalogs/BasicAnimations.js +1 -1
  107. package/lib/systems/animation/catalogs/BasicAnimations.js.map +1 -1
  108. package/lib/systems/animation/catalogs/ComboAnimations.js +1 -1
  109. package/lib/systems/animation/catalogs/ComboAnimations.js.map +1 -1
  110. package/lib/systems/animation/catalogs/DarkOpsAnimations.js +1 -1
  111. package/lib/systems/animation/catalogs/DarkOpsAnimations.js.map +1 -1
  112. package/lib/systems/animation/catalogs/DefensiveAnimations.js +2 -11
  113. package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
  114. package/lib/systems/animation/catalogs/ElbowKneeAnimations.js +1 -1
  115. package/lib/systems/animation/catalogs/ElbowKneeAnimations.js.map +1 -1
  116. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js +1 -1
  117. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
  118. package/lib/systems/animation/catalogs/GamRedirectionAnimations.js.map +1 -1
  119. package/lib/systems/animation/catalogs/GamStanceAnimations.js.map +1 -1
  120. package/lib/systems/animation/catalogs/GamTechniqueAnimations.js.map +1 -1
  121. package/lib/systems/animation/catalogs/GanStanceAnimations.js +1 -1
  122. package/lib/systems/animation/catalogs/GanStanceAnimations.js.map +1 -1
  123. package/lib/systems/animation/catalogs/GanTechniqueAnimations.js +1 -1
  124. package/lib/systems/animation/catalogs/GanTechniqueAnimations.js.map +1 -1
  125. package/lib/systems/animation/catalogs/GeonStanceAnimations.js +1 -1
  126. package/lib/systems/animation/catalogs/GeonStanceAnimations.js.map +1 -1
  127. package/lib/systems/animation/catalogs/GonTechniqueAnimations.js +1 -1
  128. package/lib/systems/animation/catalogs/GonTechniqueAnimations.js.map +1 -1
  129. package/lib/systems/animation/catalogs/GrapplingAnimations.js +1 -1
  130. package/lib/systems/animation/catalogs/GrapplingAnimations.js.map +1 -1
  131. package/lib/systems/animation/catalogs/JinStanceAnimations.js +1 -1
  132. package/lib/systems/animation/catalogs/JinStanceAnimations.js.map +1 -1
  133. package/lib/systems/animation/catalogs/JinTechniqueAnimations.js +1 -1
  134. package/lib/systems/animation/catalogs/JinTechniqueAnimations.js.map +1 -1
  135. package/lib/systems/animation/catalogs/KickAnimations.js +1 -1
  136. package/lib/systems/animation/catalogs/KickAnimations.js.map +1 -1
  137. package/lib/systems/animation/catalogs/LiStanceAnimations.js +1 -1
  138. package/lib/systems/animation/catalogs/LiStanceAnimations.js.map +1 -1
  139. package/lib/systems/animation/catalogs/LiTechniqueAnimations.js.map +1 -1
  140. package/lib/systems/animation/catalogs/MovementAnimations.js +1 -1
  141. package/lib/systems/animation/catalogs/MovementAnimations.js.map +1 -1
  142. package/lib/systems/animation/catalogs/PunchAnimations.js +1 -1
  143. package/lib/systems/animation/catalogs/PunchAnimations.js.map +1 -1
  144. package/lib/systems/animation/catalogs/SonStanceAnimations.js +1 -1
  145. package/lib/systems/animation/catalogs/SonStanceAnimations.js.map +1 -1
  146. package/lib/systems/animation/catalogs/SonTechniqueAnimations.js +1 -1
  147. package/lib/systems/animation/catalogs/SonTechniqueAnimations.js.map +1 -1
  148. package/lib/systems/animation/catalogs/SpecializedPunchAnimations.js +1 -1
  149. package/lib/systems/animation/catalogs/SpecializedPunchAnimations.js.map +1 -1
  150. package/lib/systems/animation/catalogs/StanceAnimations.js +1 -1
  151. package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
  152. package/lib/systems/animation/catalogs/StanceAttackAnimations.js +215 -272
  153. package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
  154. package/lib/systems/animation/catalogs/StanceIdleAnimations.js +2 -11
  155. package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
  156. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js +2 -2
  157. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
  158. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js +1 -1
  159. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
  160. package/lib/systems/animation/catalogs/TaeJointLockAnimations.js.map +1 -1
  161. package/lib/systems/animation/catalogs/TaeStanceAnimations.js +1 -1
  162. package/lib/systems/animation/catalogs/TaeStanceAnimations.js.map +1 -1
  163. package/lib/systems/animation/core/AnimationOptimizations.js +1 -1
  164. package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
  165. package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
  166. package/lib/systems/animation/core/AnimationRegistry.js +11 -209
  167. package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
  168. package/lib/systems/animation/core/AnimationStateMachine.js +2 -2
  169. package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
  170. package/lib/systems/animation/core/TechniqueAnimationMapper.js +1 -1
  171. package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
  172. package/lib/systems/animation/core/TechniqueAnimationMapping.js +1 -1
  173. package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
  174. package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
  175. package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
  176. package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
  177. package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
  178. package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
  179. package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
  180. package/lib/systems/combat/BalanceSystem.js.map +1 -1
  181. package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
  182. package/lib/systems/combat/CombatStateSystem.js.map +1 -1
  183. package/lib/systems/combat/GrappleSystem.js.map +1 -1
  184. package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
  185. package/lib/systems/combat/typeGuards.js.map +1 -1
  186. package/lib/systems/index.js +2 -2
  187. package/lib/systems/movement/integration.js.map +1 -1
  188. package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
  189. package/lib/systems/physics/CollisionDetection.js +1 -1
  190. package/lib/systems/physics/CollisionDetection.js.map +1 -1
  191. package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
  192. package/lib/systems/physics/MovementPhysics.js.map +1 -1
  193. package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
  194. package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
  195. package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
  196. package/lib/systems/trigram/StanceManager.js.map +1 -1
  197. package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
  198. package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
  199. package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
  200. package/lib/systems/trigram/techniques/index.js.map +1 -1
  201. package/lib/systems/trigram/types.js.map +1 -1
  202. package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
  203. package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
  204. package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
  205. package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
  206. package/lib/types/constants/designSystem.js.map +1 -1
  207. package/lib/utils/colorHelpers.js.map +1 -1
  208. package/lib/utils/colorUtils.js.map +1 -1
  209. package/lib/utils/controlMapping.js.map +1 -1
  210. package/lib/utils/deviceDetection.js.map +1 -1
  211. package/lib/utils/effectUtils.js.map +1 -1
  212. package/lib/utils/fabricTextures.js.map +1 -1
  213. package/lib/utils/htmlOverlayHelpers.js.map +1 -1
  214. package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
  215. package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
  216. package/lib/utils/safeAreaUtils.js.map +1 -1
  217. package/lib/utils/stanceHelpers.js.map +1 -1
  218. package/lib/utils/visualEffects.js.map +1 -1
  219. package/package.json +9 -9
@@ -119,7 +119,7 @@ var TAE_FLEXIBLE_GUARD_TRANSITION = MartialArtsAnimationBuilder.create("tae_flex
119
119
  * Map of all Tae stance-specific animations for easy access
120
120
  * @korean 태괘자세애니메이션맵
121
121
  */
122
- var TAE_STANCE_ANIMATIONS = new Map([
122
+ var TAE_STANCE_ANIMATIONS = /* @__PURE__ */ new Map([
123
123
  ["tae_idle_flowing", TAE_IDLE_FLOWING],
124
124
  ["tae_circular_sidestep", TAE_CIRCULAR_SIDESTEP],
125
125
  ["tae_diagonal_circular_approach", TAE_DIAGONAL_CIRCULAR_APPROACH],
@@ -1 +1 @@
1
- {"version":3,"file":"TaeStanceAnimations.js","names":[],"sources":["../../../../src/systems/animation/catalogs/TaeStanceAnimations.ts"],"sourcesContent":["/**\n * ☱ Tae (Lake) Stance-Specific Animations\n *\n * Specialized idle, movement, and guard animations for the Tae (태/Lake) trigram.\n * Embodies fluid joint manipulation and circular motion from Hapkido techniques.\n *\n * **Korean Martial Arts Context:**\n * - **무술**: 합기도 관절기 (Hapkido Joint Manipulation)\n * - **특성**: 유동적 관절기 (Fluid Joint Locks), 소원 기술 (Small Circle Techniques)\n * - **철학**: 물처럼 흐르는 힘 (Flowing Power Like Water), 호수의 적응 (Lake's Adaptation)\n * - **대표 기술**: 유수연타 (Flowing Water Strikes), 손목꺾기 (Wrist Lock), 팔꿈치 제어 (Elbow Control)\n *\n * @module systems/animation/catalogs/TaeStanceAnimations\n * @category Animation\n * @korean 태괘자세애니메이션\n */\n\nimport { BoneName, type SkeletalAnimation } from \"@/types/skeletal\";\nimport { MartialArtsAnimationBuilder } from \"../builders/MartialArtsAnimationBuilder\";\n\n// Import Hapkido joint lock techniques (합기도 관절기)\nimport {\n TAE_WRIST_LOCK_SEQUENCE,\n TAE_ELBOW_CONTROL,\n TAE_FINGER_LOCK,\n TAE_FLOWING_COUNTER,\n TAE_FLOWING_STRIKES,\n TAE_SMALL_CIRCLE,\n TAE_SHOULDER_LOCK,\n TAE_ARM_BAR,\n} from \"./TaeJointLockAnimations\";\n\n/**\n * Anatomical safety constants for Tae (Lake) trigram animations\n *\n * These limits document safe physiological ranges for Korean martial arts movements.\n * The constants serve as reference documentation validated by Korean martial arts experts\n * and inform the design of all Tae animations to ensure realistic, safe joint mechanics.\n *\n * Note: These are documentation constants. The actual animation rotations are designed\n * to stay well within these limits naturally through authentic Hapkido biomechanics.\n */\n\n// ═══════════════════════════════════════════════════════════════════════════\n// ☱ TAE IDLE FLOWING ANIMATION (태괘 유동 자세)\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Tae Idle Flowing Animation\n *\n * **Korean**: 태괘 유동 자세 (Tae-gwae Yudong Jase)\n * **Philosophy**: Embodying lake's fluidity through gentle circular breathing\n *\n * Characteristics:\n * - Gentle circular shoulder movement\n * - Relaxed mid-level guard position\n * - Weight centered and stable\n * - Hands open and ready to grasp\n *\n * Animation Cycle:\n * - 0ms: Neutral position with mid-level guard\n * - 1250ms: Circular shift (left shoulder forward, right back)\n * - 2500ms: Return to neutral (complete cycle)\n *\n * @korean 태괘유동자세\n * @duration 2500ms (2.5 second cycle)\n * @category Idle Animation\n */\nexport const TAE_IDLE_FLOWING: SkeletalAnimation =\n MartialArtsAnimationBuilder.create(\n \"tae_idle_flowing\",\n \"태괘 유동 자세\"\n )\n .asIdle(2.5, true)\n // Keyframe 0ms: Neutral mid-level guard (baseline)\n .at(0)\n .rotate(BoneName.PELVIS, 0, 0, 0) // Neutral pelvis\n .rotate(BoneName.SPINE_UPPER, 0, 0, 0) // Neutral spine\n .rotate(BoneName.SHOULDER_L, -0.52, -0.17, 0.26) // -30°, -10°, 15° (mid-level flexible guard)\n .rotate(BoneName.SHOULDER_R, -0.52, 0.17, -0.26) // -30°, 10°, -15°\n .rotate(BoneName.ELBOW_L, 0, 0, -1.4) // -80° (flexed guard)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.4) // 80° (flexed guard)\n .rotate(BoneName.WRIST_L, 0, 0, 0) // Neutral wrists\n .rotate(BoneName.WRIST_R, 0, 0, 0)\n .rotate(BoneName.HEAD, 0, 0, 0) // Head neutral, aware\n .position(BoneName.PELVIS, 0, 0, 0)\n .done<MartialArtsAnimationBuilder>()\n // Keyframe 1250ms: Circular shift (subtle flowing motion)\n .at(1.25)\n .rotate(BoneName.SPINE_UPPER, 0, 0.09, 0) // 0°, 5° (slight rotation creating circular feel)\n .rotate(BoneName.SHOULDER_L, -0.48, -0.09, 0.17) // -27.5°, -5°, 10° (left forward slightly)\n .rotate(BoneName.SHOULDER_R, -0.57, 0.26, -0.17) // -32.5°, 15°, -10° (right back slightly)\n .rotate(BoneName.ELBOW_L, 0, 0, -1.31) // -75° (slight extension)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.48) // 85° (slight flexion)\n .rotate(BoneName.WRIST_L, 0, 0.09, 0) // 0°, 5° (gentle circular motion)\n .rotate(BoneName.WRIST_R, 0, -0.09, 0) // 0°, -5°\n .rotate(BoneName.PELVIS, 0, 0.05, 0) // 0°, 3° (subtle weight shift)\n .position(BoneName.PELVIS, -0.02, 0, 0) // Minimal lateral shift\n .done<MartialArtsAnimationBuilder>()\n // Keyframe 2500ms: Return to neutral (complete cycle)\n .at(2.5)\n .rotate(BoneName.SPINE_UPPER, 0, 0, 0)\n .rotate(BoneName.SHOULDER_L, -0.52, -0.17, 0.26)\n .rotate(BoneName.SHOULDER_R, -0.52, 0.17, -0.26)\n .rotate(BoneName.ELBOW_L, 0, 0, -1.4)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.4)\n .rotate(BoneName.WRIST_L, 0, 0, 0)\n .rotate(BoneName.WRIST_R, 0, 0, 0)\n .rotate(BoneName.HEAD, 0, 0, 0)\n .rotate(BoneName.PELVIS, 0, 0, 0)\n .position(BoneName.PELVIS, 0, 0, 0)\n .done<MartialArtsAnimationBuilder>()\n .build();\n\n// ═══════════════════════════════════════════════════════════════════════════\n// ☱ TAE CIRCULAR SIDESTEP (원형 측면보)\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Tae Circular Sidestep Animation\n *\n * **Korean**: 원형 측면보 (Wonhyeong Cheukmyeonbo) - Circular Sidestep\n * **Technique**: Arc-shaped lateral movement, hip-led\n *\n * Characteristics:\n * - Circular arc path (not straight lateral)\n * - Hip leads the movement\n * - Maintains flexible guard throughout\n * - Weight shifts smoothly\n *\n * Animation Phases:\n * - 0-180ms: Weight shift and hip initiation\n * - 180-400ms: Circular arc movement\n * - 400-550ms: Landing and settling\n *\n * @korean 원형측면보\n * @duration 550ms\n * @category Movement Animation\n */\nexport const TAE_CIRCULAR_SIDESTEP: SkeletalAnimation =\n MartialArtsAnimationBuilder.create(\n \"tae_circular_sidestep\",\n \"원형 측면보\"\n )\n .asMovement(0.55, false)\n // Phase 1: Initial weight shift (0ms)\n .at(0)\n .rotate(BoneName.PELVIS, 0, -0.17, 0) // 0°, -10° (hip begins rotation)\n .rotate(BoneName.KNEE_R, -0.26, 0, 0) // -15° (rear leg loads)\n .rotate(BoneName.KNEE_L, -0.35, 0, 0) // -20° (lead leg ready)\n .rotate(BoneName.SHOULDER_L, -0.52, -0.17, 0.26) // Guard maintained\n .rotate(BoneName.SHOULDER_R, -0.52, 0.17, -0.26)\n .rotate(BoneName.ELBOW_L, 0, 0, -1.4)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.4)\n .position(BoneName.PELVIS, 0, 0, 0)\n .done<MartialArtsAnimationBuilder>()\n // Phase 2: Circular arc movement (275ms)\n .at(0.275)\n .rotate(BoneName.PELVIS, 0, -0.35, 0) // 0°, -20° (maximum hip rotation)\n .rotate(BoneName.KNEE_R, -0.17, 0, 0) // -10° (rear leg extends)\n .rotate(BoneName.KNEE_L, -0.44, 0, 0) // -25° (lead leg pushes)\n .rotate(BoneName.SHOULDER_L, -0.52, -0.17, 0.26) // Guard stable\n .rotate(BoneName.SHOULDER_R, -0.52, 0.17, -0.26)\n .position(BoneName.PELVIS, -0.18, 0.02, -0.03) // Arc trajectory (lateral + slight back)\n .done<MartialArtsAnimationBuilder>()\n // Phase 3: Landing and settling (550ms)\n .at(0.55)\n .rotate(BoneName.PELVIS, 0, -0.26, 0) // 0°, -15° (settled)\n .rotate(BoneName.KNEE_R, -0.26, 0, 0) // -15° (stable)\n .rotate(BoneName.KNEE_L, -0.35, 0, 0) // -20° (grounded)\n .rotate(BoneName.SHOULDER_L, -0.52, -0.17, 0.26) // Guard maintained\n .rotate(BoneName.SHOULDER_R, -0.52, 0.17, -0.26)\n .rotate(BoneName.ELBOW_L, 0, 0, -1.4)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.4)\n .position(BoneName.PELVIS, -0.25, 0, 0) // Final lateral position\n .done<MartialArtsAnimationBuilder>()\n .build();\n\n// ═══════════════════════════════════════════════════════════════════════════\n// ☱ TAE DIAGONAL CIRCULAR APPROACH (대각선 원형 접근)\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Tae Diagonal Circular Approach Animation\n *\n * **Korean**: 대각선 원형 접근 (Daegakseon Wonhyeong Jeobgeun)\n * **Technique**: 45° curved approach with hands extending\n *\n * Characteristics:\n * - 45° diagonal angle with curved path\n * - Hands extend forward for joint control\n * - Hip rotation coordinates with step\n * - Smooth weight transfer\n *\n * Animation Phases:\n * - 0-233ms: Initial diagonal pivot\n * - 233-500ms: Curved approach with extending hands\n * - 500-667ms: Landing in engagement position\n *\n * @korean 대각선원형접근\n * @duration 667ms\n * @category Movement Animation\n */\nexport const TAE_DIAGONAL_CIRCULAR_APPROACH: SkeletalAnimation =\n MartialArtsAnimationBuilder.create(\n \"tae_diagonal_circular_approach\",\n \"대각선 원형 접근\"\n )\n .asMovement(0.667, false)\n // Phase 1: Initial diagonal pivot (0ms)\n .at(0)\n .rotate(BoneName.PELVIS, 0, -0.79, 0) // 0°, -45° (diagonal rotation)\n .rotate(BoneName.KNEE_R, -0.35, 0, 0) // -20° (rear leg ready)\n .rotate(BoneName.KNEE_L, -0.26, 0, 0) // -15° (lead leg ready)\n .rotate(BoneName.SHOULDER_L, -0.44, -0.26, 0.35) // -25°, -15°, 20° (hands begin extending)\n .rotate(BoneName.SHOULDER_R, -0.44, 0.26, -0.35) // -25°, 15°, -20°\n .rotate(BoneName.ELBOW_L, 0, 0, -1.05) // -60° (extending)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.05) // 60° (extending)\n .position(BoneName.PELVIS, 0, 0, 0)\n .done<MartialArtsAnimationBuilder>()\n // Phase 2: Curved approach with extending hands (361ms)\n .at(0.361)\n .rotate(BoneName.PELVIS, 0, -0.61, 0) // 0°, -35° (unwinding)\n .rotate(BoneName.KNEE_R, -0.17, 0, 0) // -10° (extending)\n .rotate(BoneName.KNEE_L, -0.44, 0, 0) // -25° (pushing)\n .rotate(BoneName.SHOULDER_L, -0.35, -0.35, 0.44) // -20°, -20°, 25° (reaching)\n .rotate(BoneName.SHOULDER_R, -0.35, 0.35, -0.44) // -20°, 20°, -25°\n .rotate(BoneName.ELBOW_L, 0, 0, -0.7) // -40° (extending more)\n .rotate(BoneName.ELBOW_R, 0, 0, 0.7) // 40° (extending more)\n .position(BoneName.PELVIS, -0.2, 0.02, 0.2) // Curved diagonal path\n .done<MartialArtsAnimationBuilder>()\n // Phase 3: Landing in engagement position (667ms)\n .at(0.667)\n .rotate(BoneName.PELVIS, 0, -0.79, 0) // 0°, -45° (settled diagonal)\n .rotate(BoneName.KNEE_R, -0.26, 0, 0) // -15° (stable)\n .rotate(BoneName.KNEE_L, -0.35, 0, 0) // -20° (grounded)\n .rotate(BoneName.SHOULDER_L, -0.26, -0.44, 0.52) // -15°, -25°, 30° (hands extended)\n .rotate(BoneName.SHOULDER_R, -0.26, 0.44, -0.52) // -15°, 25°, -30°\n .rotate(BoneName.ELBOW_L, 0, 0, -0.52) // -30° (ready to engage)\n .rotate(BoneName.ELBOW_R, 0, 0, 0.52) // 30° (ready to engage)\n .rotate(BoneName.WRIST_L, 0.09, 0, -0.09) // 5°, 0°, -5° (hands ready to grasp)\n .rotate(BoneName.WRIST_R, 0.09, 0, 0.09) // 5°, 0°, 5°\n .position(BoneName.PELVIS, -0.3, 0, 0.3) // Diagonal 45° position\n .done<MartialArtsAnimationBuilder>()\n .build();\n\n// ═══════════════════════════════════════════════════════════════════════════\n// ☱ TAE FLEXIBLE GUARD TRANSITION (호수 방어 전환)\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Tae Flexible Guard Transition Animation\n *\n * **Korean**: 호수 방어 전환 (Hosu Bangeo Jeonhwan) - Lake Guard Transition\n * **Technique**: Transition into Tae's characteristic flexible mid-level guard\n *\n * Characteristics:\n * - Smooth transition to mid-level guard\n * - Hands at chest/solar plexus height\n * - Elbows bent at 80° for flexibility\n * - Weight evenly distributed\n *\n * Animation Phases:\n * - 0-150ms: Begin transition\n * - 150-300ms: Settle into Tae guard\n *\n * @korean 호수방어전환\n * @duration 300ms\n * @category Stance Animation\n */\nexport const TAE_FLEXIBLE_GUARD_TRANSITION: SkeletalAnimation =\n MartialArtsAnimationBuilder.create(\n \"tae_flexible_guard_transition\",\n \"호수 방어 전환\"\n )\n .asStance(0.3, false)\n // Phase 1: Begin transition (0ms)\n .at(0)\n .rotate(BoneName.SHOULDER_L, -0.44, -0.09, 0.22) // -25°, -5°, 12.5° (moving toward guard)\n .rotate(BoneName.SHOULDER_R, -0.44, 0.09, -0.22) // -25°, 5°, -12.5°\n .rotate(BoneName.ELBOW_L, 0, 0, -1.22) // -70° (transitioning)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.22) // 70° (transitioning)\n .rotate(BoneName.PELVIS, 0, 0, 0) // Neutral pelvis\n .position(BoneName.PELVIS, 0, 0, 0)\n .done<MartialArtsAnimationBuilder>()\n // Phase 2: Settle into Tae guard (300ms)\n .at(0.3)\n .rotate(BoneName.SHOULDER_L, -0.52, -0.17, 0.26) // -30°, -10°, 15° (Tae guard)\n .rotate(BoneName.SHOULDER_R, -0.52, 0.17, -0.26) // -30°, 10°, -15°\n .rotate(BoneName.ELBOW_L, 0, 0, -1.4) // -80° (Tae guard angle)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.4) // 80° (Tae guard angle)\n .rotate(BoneName.WRIST_L, 0, 0, 0) // Neutral wrists\n .rotate(BoneName.WRIST_R, 0, 0, 0)\n .rotate(BoneName.KNEE_L, -0.26, 0, 0) // -15° (stable stance)\n .rotate(BoneName.KNEE_R, -0.26, 0, 0) // -15° (stable stance)\n .position(BoneName.PELVIS, 0, 0, 0)\n .done<MartialArtsAnimationBuilder>()\n .build();\n\n// ═══════════════════════════════════════════════════════════════════════════\n// EXPORTS\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Map of all Tae stance-specific animations for easy access\n * @korean 태괘자세애니메이션맵\n */\nexport const TAE_STANCE_ANIMATIONS: ReadonlyMap<string, SkeletalAnimation> = new Map([\n // Idle & Movement\n [\"tae_idle_flowing\", TAE_IDLE_FLOWING],\n [\"tae_circular_sidestep\", TAE_CIRCULAR_SIDESTEP],\n [\"tae_diagonal_circular_approach\", TAE_DIAGONAL_CIRCULAR_APPROACH],\n \n // Stance Transitions\n [\"tae_flexible_guard_transition\", TAE_FLEXIBLE_GUARD_TRANSITION],\n \n // Hapkido Joint Lock Techniques (합기도 관절기)\n [\"tae_wrist_lock_sequence\", TAE_WRIST_LOCK_SEQUENCE],\n [\"tae_elbow_control\", TAE_ELBOW_CONTROL],\n [\"tae_finger_lock\", TAE_FINGER_LOCK],\n [\"tae_flowing_counter\", TAE_FLOWING_COUNTER],\n [\"tae_flowing_strikes\", TAE_FLOWING_STRIKES],\n [\"tae_small_circle\", TAE_SMALL_CIRCLE],\n [\"tae_shoulder_lock\", TAE_SHOULDER_LOCK],\n [\"tae_arm_bar\", TAE_ARM_BAR],\n]);\n"],"mappings":"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"}
1
+ {"version":3,"file":"TaeStanceAnimations.js","names":[],"sources":["../../../../src/systems/animation/catalogs/TaeStanceAnimations.ts"],"sourcesContent":["/**\n * ☱ Tae (Lake) Stance-Specific Animations\n *\n * Specialized idle, movement, and guard animations for the Tae (태/Lake) trigram.\n * Embodies fluid joint manipulation and circular motion from Hapkido techniques.\n *\n * **Korean Martial Arts Context:**\n * - **무술**: 합기도 관절기 (Hapkido Joint Manipulation)\n * - **특성**: 유동적 관절기 (Fluid Joint Locks), 소원 기술 (Small Circle Techniques)\n * - **철학**: 물처럼 흐르는 힘 (Flowing Power Like Water), 호수의 적응 (Lake's Adaptation)\n * - **대표 기술**: 유수연타 (Flowing Water Strikes), 손목꺾기 (Wrist Lock), 팔꿈치 제어 (Elbow Control)\n *\n * @module systems/animation/catalogs/TaeStanceAnimations\n * @category Animation\n * @korean 태괘자세애니메이션\n */\n\nimport { BoneName, type SkeletalAnimation } from \"@/types/skeletal\";\nimport { MartialArtsAnimationBuilder } from \"../builders/MartialArtsAnimationBuilder\";\n\n// Import Hapkido joint lock techniques (합기도 관절기)\nimport {\n TAE_WRIST_LOCK_SEQUENCE,\n TAE_ELBOW_CONTROL,\n TAE_FINGER_LOCK,\n TAE_FLOWING_COUNTER,\n TAE_FLOWING_STRIKES,\n TAE_SMALL_CIRCLE,\n TAE_SHOULDER_LOCK,\n TAE_ARM_BAR,\n} from \"./TaeJointLockAnimations\";\n\n/**\n * Anatomical safety constants for Tae (Lake) trigram animations\n *\n * These limits document safe physiological ranges for Korean martial arts movements.\n * The constants serve as reference documentation validated by Korean martial arts experts\n * and inform the design of all Tae animations to ensure realistic, safe joint mechanics.\n *\n * Note: These are documentation constants. The actual animation rotations are designed\n * to stay well within these limits naturally through authentic Hapkido biomechanics.\n */\n\n// ═══════════════════════════════════════════════════════════════════════════\n// ☱ TAE IDLE FLOWING ANIMATION (태괘 유동 자세)\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Tae Idle Flowing Animation\n *\n * **Korean**: 태괘 유동 자세 (Tae-gwae Yudong Jase)\n * **Philosophy**: Embodying lake's fluidity through gentle circular breathing\n *\n * Characteristics:\n * - Gentle circular shoulder movement\n * - Relaxed mid-level guard position\n * - Weight centered and stable\n * - Hands open and ready to grasp\n *\n * Animation Cycle:\n * - 0ms: Neutral position with mid-level guard\n * - 1250ms: Circular shift (left shoulder forward, right back)\n * - 2500ms: Return to neutral (complete cycle)\n *\n * @korean 태괘유동자세\n * @duration 2500ms (2.5 second cycle)\n * @category Idle Animation\n */\nexport const TAE_IDLE_FLOWING: SkeletalAnimation =\n MartialArtsAnimationBuilder.create(\n \"tae_idle_flowing\",\n \"태괘 유동 자세\"\n )\n .asIdle(2.5, true)\n // Keyframe 0ms: Neutral mid-level guard (baseline)\n .at(0)\n .rotate(BoneName.PELVIS, 0, 0, 0) // Neutral pelvis\n .rotate(BoneName.SPINE_UPPER, 0, 0, 0) // Neutral spine\n .rotate(BoneName.SHOULDER_L, -0.52, -0.17, 0.26) // -30°, -10°, 15° (mid-level flexible guard)\n .rotate(BoneName.SHOULDER_R, -0.52, 0.17, -0.26) // -30°, 10°, -15°\n .rotate(BoneName.ELBOW_L, 0, 0, -1.4) // -80° (flexed guard)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.4) // 80° (flexed guard)\n .rotate(BoneName.WRIST_L, 0, 0, 0) // Neutral wrists\n .rotate(BoneName.WRIST_R, 0, 0, 0)\n .rotate(BoneName.HEAD, 0, 0, 0) // Head neutral, aware\n .position(BoneName.PELVIS, 0, 0, 0)\n .done<MartialArtsAnimationBuilder>()\n // Keyframe 1250ms: Circular shift (subtle flowing motion)\n .at(1.25)\n .rotate(BoneName.SPINE_UPPER, 0, 0.09, 0) // 0°, 5° (slight rotation creating circular feel)\n .rotate(BoneName.SHOULDER_L, -0.48, -0.09, 0.17) // -27.5°, -5°, 10° (left forward slightly)\n .rotate(BoneName.SHOULDER_R, -0.57, 0.26, -0.17) // -32.5°, 15°, -10° (right back slightly)\n .rotate(BoneName.ELBOW_L, 0, 0, -1.31) // -75° (slight extension)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.48) // 85° (slight flexion)\n .rotate(BoneName.WRIST_L, 0, 0.09, 0) // 0°, 5° (gentle circular motion)\n .rotate(BoneName.WRIST_R, 0, -0.09, 0) // 0°, -5°\n .rotate(BoneName.PELVIS, 0, 0.05, 0) // 0°, 3° (subtle weight shift)\n .position(BoneName.PELVIS, -0.02, 0, 0) // Minimal lateral shift\n .done<MartialArtsAnimationBuilder>()\n // Keyframe 2500ms: Return to neutral (complete cycle)\n .at(2.5)\n .rotate(BoneName.SPINE_UPPER, 0, 0, 0)\n .rotate(BoneName.SHOULDER_L, -0.52, -0.17, 0.26)\n .rotate(BoneName.SHOULDER_R, -0.52, 0.17, -0.26)\n .rotate(BoneName.ELBOW_L, 0, 0, -1.4)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.4)\n .rotate(BoneName.WRIST_L, 0, 0, 0)\n .rotate(BoneName.WRIST_R, 0, 0, 0)\n .rotate(BoneName.HEAD, 0, 0, 0)\n .rotate(BoneName.PELVIS, 0, 0, 0)\n .position(BoneName.PELVIS, 0, 0, 0)\n .done<MartialArtsAnimationBuilder>()\n .build();\n\n// ═══════════════════════════════════════════════════════════════════════════\n// ☱ TAE CIRCULAR SIDESTEP (원형 측면보)\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Tae Circular Sidestep Animation\n *\n * **Korean**: 원형 측면보 (Wonhyeong Cheukmyeonbo) - Circular Sidestep\n * **Technique**: Arc-shaped lateral movement, hip-led\n *\n * Characteristics:\n * - Circular arc path (not straight lateral)\n * - Hip leads the movement\n * - Maintains flexible guard throughout\n * - Weight shifts smoothly\n *\n * Animation Phases:\n * - 0-180ms: Weight shift and hip initiation\n * - 180-400ms: Circular arc movement\n * - 400-550ms: Landing and settling\n *\n * @korean 원형측면보\n * @duration 550ms\n * @category Movement Animation\n */\nexport const TAE_CIRCULAR_SIDESTEP: SkeletalAnimation =\n MartialArtsAnimationBuilder.create(\n \"tae_circular_sidestep\",\n \"원형 측면보\"\n )\n .asMovement(0.55, false)\n // Phase 1: Initial weight shift (0ms)\n .at(0)\n .rotate(BoneName.PELVIS, 0, -0.17, 0) // 0°, -10° (hip begins rotation)\n .rotate(BoneName.KNEE_R, -0.26, 0, 0) // -15° (rear leg loads)\n .rotate(BoneName.KNEE_L, -0.35, 0, 0) // -20° (lead leg ready)\n .rotate(BoneName.SHOULDER_L, -0.52, -0.17, 0.26) // Guard maintained\n .rotate(BoneName.SHOULDER_R, -0.52, 0.17, -0.26)\n .rotate(BoneName.ELBOW_L, 0, 0, -1.4)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.4)\n .position(BoneName.PELVIS, 0, 0, 0)\n .done<MartialArtsAnimationBuilder>()\n // Phase 2: Circular arc movement (275ms)\n .at(0.275)\n .rotate(BoneName.PELVIS, 0, -0.35, 0) // 0°, -20° (maximum hip rotation)\n .rotate(BoneName.KNEE_R, -0.17, 0, 0) // -10° (rear leg extends)\n .rotate(BoneName.KNEE_L, -0.44, 0, 0) // -25° (lead leg pushes)\n .rotate(BoneName.SHOULDER_L, -0.52, -0.17, 0.26) // Guard stable\n .rotate(BoneName.SHOULDER_R, -0.52, 0.17, -0.26)\n .position(BoneName.PELVIS, -0.18, 0.02, -0.03) // Arc trajectory (lateral + slight back)\n .done<MartialArtsAnimationBuilder>()\n // Phase 3: Landing and settling (550ms)\n .at(0.55)\n .rotate(BoneName.PELVIS, 0, -0.26, 0) // 0°, -15° (settled)\n .rotate(BoneName.KNEE_R, -0.26, 0, 0) // -15° (stable)\n .rotate(BoneName.KNEE_L, -0.35, 0, 0) // -20° (grounded)\n .rotate(BoneName.SHOULDER_L, -0.52, -0.17, 0.26) // Guard maintained\n .rotate(BoneName.SHOULDER_R, -0.52, 0.17, -0.26)\n .rotate(BoneName.ELBOW_L, 0, 0, -1.4)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.4)\n .position(BoneName.PELVIS, -0.25, 0, 0) // Final lateral position\n .done<MartialArtsAnimationBuilder>()\n .build();\n\n// ═══════════════════════════════════════════════════════════════════════════\n// ☱ TAE DIAGONAL CIRCULAR APPROACH (대각선 원형 접근)\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Tae Diagonal Circular Approach Animation\n *\n * **Korean**: 대각선 원형 접근 (Daegakseon Wonhyeong Jeobgeun)\n * **Technique**: 45° curved approach with hands extending\n *\n * Characteristics:\n * - 45° diagonal angle with curved path\n * - Hands extend forward for joint control\n * - Hip rotation coordinates with step\n * - Smooth weight transfer\n *\n * Animation Phases:\n * - 0-233ms: Initial diagonal pivot\n * - 233-500ms: Curved approach with extending hands\n * - 500-667ms: Landing in engagement position\n *\n * @korean 대각선원형접근\n * @duration 667ms\n * @category Movement Animation\n */\nexport const TAE_DIAGONAL_CIRCULAR_APPROACH: SkeletalAnimation =\n MartialArtsAnimationBuilder.create(\n \"tae_diagonal_circular_approach\",\n \"대각선 원형 접근\"\n )\n .asMovement(0.667, false)\n // Phase 1: Initial diagonal pivot (0ms)\n .at(0)\n .rotate(BoneName.PELVIS, 0, -0.79, 0) // 0°, -45° (diagonal rotation)\n .rotate(BoneName.KNEE_R, -0.35, 0, 0) // -20° (rear leg ready)\n .rotate(BoneName.KNEE_L, -0.26, 0, 0) // -15° (lead leg ready)\n .rotate(BoneName.SHOULDER_L, -0.44, -0.26, 0.35) // -25°, -15°, 20° (hands begin extending)\n .rotate(BoneName.SHOULDER_R, -0.44, 0.26, -0.35) // -25°, 15°, -20°\n .rotate(BoneName.ELBOW_L, 0, 0, -1.05) // -60° (extending)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.05) // 60° (extending)\n .position(BoneName.PELVIS, 0, 0, 0)\n .done<MartialArtsAnimationBuilder>()\n // Phase 2: Curved approach with extending hands (361ms)\n .at(0.361)\n .rotate(BoneName.PELVIS, 0, -0.61, 0) // 0°, -35° (unwinding)\n .rotate(BoneName.KNEE_R, -0.17, 0, 0) // -10° (extending)\n .rotate(BoneName.KNEE_L, -0.44, 0, 0) // -25° (pushing)\n .rotate(BoneName.SHOULDER_L, -0.35, -0.35, 0.44) // -20°, -20°, 25° (reaching)\n .rotate(BoneName.SHOULDER_R, -0.35, 0.35, -0.44) // -20°, 20°, -25°\n .rotate(BoneName.ELBOW_L, 0, 0, -0.7) // -40° (extending more)\n .rotate(BoneName.ELBOW_R, 0, 0, 0.7) // 40° (extending more)\n .position(BoneName.PELVIS, -0.2, 0.02, 0.2) // Curved diagonal path\n .done<MartialArtsAnimationBuilder>()\n // Phase 3: Landing in engagement position (667ms)\n .at(0.667)\n .rotate(BoneName.PELVIS, 0, -0.79, 0) // 0°, -45° (settled diagonal)\n .rotate(BoneName.KNEE_R, -0.26, 0, 0) // -15° (stable)\n .rotate(BoneName.KNEE_L, -0.35, 0, 0) // -20° (grounded)\n .rotate(BoneName.SHOULDER_L, -0.26, -0.44, 0.52) // -15°, -25°, 30° (hands extended)\n .rotate(BoneName.SHOULDER_R, -0.26, 0.44, -0.52) // -15°, 25°, -30°\n .rotate(BoneName.ELBOW_L, 0, 0, -0.52) // -30° (ready to engage)\n .rotate(BoneName.ELBOW_R, 0, 0, 0.52) // 30° (ready to engage)\n .rotate(BoneName.WRIST_L, 0.09, 0, -0.09) // 5°, 0°, -5° (hands ready to grasp)\n .rotate(BoneName.WRIST_R, 0.09, 0, 0.09) // 5°, 0°, 5°\n .position(BoneName.PELVIS, -0.3, 0, 0.3) // Diagonal 45° position\n .done<MartialArtsAnimationBuilder>()\n .build();\n\n// ═══════════════════════════════════════════════════════════════════════════\n// ☱ TAE FLEXIBLE GUARD TRANSITION (호수 방어 전환)\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Tae Flexible Guard Transition Animation\n *\n * **Korean**: 호수 방어 전환 (Hosu Bangeo Jeonhwan) - Lake Guard Transition\n * **Technique**: Transition into Tae's characteristic flexible mid-level guard\n *\n * Characteristics:\n * - Smooth transition to mid-level guard\n * - Hands at chest/solar plexus height\n * - Elbows bent at 80° for flexibility\n * - Weight evenly distributed\n *\n * Animation Phases:\n * - 0-150ms: Begin transition\n * - 150-300ms: Settle into Tae guard\n *\n * @korean 호수방어전환\n * @duration 300ms\n * @category Stance Animation\n */\nexport const TAE_FLEXIBLE_GUARD_TRANSITION: SkeletalAnimation =\n MartialArtsAnimationBuilder.create(\n \"tae_flexible_guard_transition\",\n \"호수 방어 전환\"\n )\n .asStance(0.3, false)\n // Phase 1: Begin transition (0ms)\n .at(0)\n .rotate(BoneName.SHOULDER_L, -0.44, -0.09, 0.22) // -25°, -5°, 12.5° (moving toward guard)\n .rotate(BoneName.SHOULDER_R, -0.44, 0.09, -0.22) // -25°, 5°, -12.5°\n .rotate(BoneName.ELBOW_L, 0, 0, -1.22) // -70° (transitioning)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.22) // 70° (transitioning)\n .rotate(BoneName.PELVIS, 0, 0, 0) // Neutral pelvis\n .position(BoneName.PELVIS, 0, 0, 0)\n .done<MartialArtsAnimationBuilder>()\n // Phase 2: Settle into Tae guard (300ms)\n .at(0.3)\n .rotate(BoneName.SHOULDER_L, -0.52, -0.17, 0.26) // -30°, -10°, 15° (Tae guard)\n .rotate(BoneName.SHOULDER_R, -0.52, 0.17, -0.26) // -30°, 10°, -15°\n .rotate(BoneName.ELBOW_L, 0, 0, -1.4) // -80° (Tae guard angle)\n .rotate(BoneName.ELBOW_R, 0, 0, 1.4) // 80° (Tae guard angle)\n .rotate(BoneName.WRIST_L, 0, 0, 0) // Neutral wrists\n .rotate(BoneName.WRIST_R, 0, 0, 0)\n .rotate(BoneName.KNEE_L, -0.26, 0, 0) // -15° (stable stance)\n .rotate(BoneName.KNEE_R, -0.26, 0, 0) // -15° (stable stance)\n .position(BoneName.PELVIS, 0, 0, 0)\n .done<MartialArtsAnimationBuilder>()\n .build();\n\n// ═══════════════════════════════════════════════════════════════════════════\n// EXPORTS\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Map of all Tae stance-specific animations for easy access\n * @korean 태괘자세애니메이션맵\n */\nexport const TAE_STANCE_ANIMATIONS: ReadonlyMap<string, SkeletalAnimation> = new Map([\n // Idle & Movement\n [\"tae_idle_flowing\", TAE_IDLE_FLOWING],\n [\"tae_circular_sidestep\", TAE_CIRCULAR_SIDESTEP],\n [\"tae_diagonal_circular_approach\", TAE_DIAGONAL_CIRCULAR_APPROACH],\n \n // Stance Transitions\n [\"tae_flexible_guard_transition\", TAE_FLEXIBLE_GUARD_TRANSITION],\n \n // Hapkido Joint Lock Techniques (합기도 관절기)\n [\"tae_wrist_lock_sequence\", TAE_WRIST_LOCK_SEQUENCE],\n [\"tae_elbow_control\", TAE_ELBOW_CONTROL],\n [\"tae_finger_lock\", TAE_FINGER_LOCK],\n [\"tae_flowing_counter\", TAE_FLOWING_COUNTER],\n [\"tae_flowing_strikes\", TAE_FLOWING_STRIKES],\n [\"tae_small_circle\", TAE_SMALL_CIRCLE],\n [\"tae_shoulder_lock\", TAE_SHOULDER_LOCK],\n [\"tae_arm_bar\", TAE_ARM_BAR],\n]);\n"],"mappings":"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"}
@@ -224,7 +224,7 @@ function interpolateKeyframeCached(animationId, animation, time) {
224
224
  interpolatedMuscles = /* @__PURE__ */ new Map();
225
225
  const prevMuscles = prevKeyframe.muscleActivations ?? /* @__PURE__ */ new Map();
226
226
  const nextMuscles = nextKeyframe.muscleActivations ?? /* @__PURE__ */ new Map();
227
- new Set([...prevMuscles.keys(), ...nextMuscles.keys()]).forEach((muscle) => {
227
+ (/* @__PURE__ */ new Set([...prevMuscles.keys(), ...nextMuscles.keys()])).forEach((muscle) => {
228
228
  const prevVal = prevMuscles.get(muscle) ?? 0;
229
229
  const nextVal = nextMuscles.get(muscle) ?? 0;
230
230
  interpolatedMuscles?.set(muscle, prevVal + (nextVal - prevVal) * t);
@@ -1 +1 @@
1
- {"version":3,"file":"AnimationOptimizations.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationOptimizations.ts"],"sourcesContent":["/**\n * Optimized Animation System with Caching and Batch Processing\n *\n * High-performance animation pipeline optimizations:\n * - Keyframe caching to avoid redundant interpolations\n * - Batch bone transformation updates\n * - Dirty flag optimization\n * - Precomputed interpolation curves\n *\n * Target: Reduce animation overhead from ~8ms to <5ms per frame\n *\n * @module systems/animation/AnimationOptimizations\n * @category Animation System\n * @korean 애니메이션최적화\n */\n\nimport type {\n AnimationKeyframe,\n SkeletalAnimation,\n SkeletalRig,\n} from \"@/types/skeletal\";\nimport { ThreeObjectPools } from \"@/utils/threeObjectPool\";\nimport * as THREE from \"three\";\n\n/**\n * Cached keyframe with interpolated values\n * Reduces redundant interpolation calculations\n *\n * @korean 캐시된키프레임\n */\ninterface CachedKeyframe {\n /** Original keyframe */\n readonly keyframe: AnimationKeyframe;\n /** Cache timestamp (for invalidation) */\n readonly timestamp: number;\n /** Interpolated bone rotations (Map to avoid allocations) */\n readonly rotations: Map<string, THREE.Euler>;\n /** Interpolated bone positions (if animated) */\n readonly positions: Map<string, THREE.Vector3>;\n}\n\n/**\n * Animation cache entry\n *\n * @korean 애니메이션캐시항목\n */\ninterface AnimationCache {\n /** Animation being cached */\n readonly animation: SkeletalAnimation;\n /** Cached keyframes by time */\n readonly keyframes: Map<number, CachedKeyframe>;\n /** Last access time (for LRU eviction) */\n lastAccessTime: number;\n}\n\n/**\n * Animation Cache Manager\n *\n * Caches interpolated keyframes to avoid redundant calculations.\n * Uses LRU eviction when cache is full.\n *\n * @korean 애니메이션캐시관리자\n */\nclass AnimationCacheManager {\n private cache = new Map<string, AnimationCache>();\n private readonly maxCacheSize: number;\n private readonly maxKeyframesPerAnimation: number;\n\n constructor(maxCacheSize = 50, maxKeyframesPerAnimation = 200) {\n this.maxCacheSize = maxCacheSize;\n this.maxKeyframesPerAnimation = maxKeyframesPerAnimation;\n }\n\n /**\n * Get cached keyframe or create new entry\n *\n * @param animationId - Animation identifier\n * @param animation - Animation data (reserved for future cache invalidation logic)\n * @param time - Current time\n * @returns Cached keyframe or null if not cached\n */\n get(\n animationId: string,\n animation: SkeletalAnimation,\n time: number,\n ): CachedKeyframe | null {\n // Mark parameter as used for future extensibility (e.g., cache invalidation)\n void animation;\n const entry = this.cache.get(animationId);\n if (!entry) {\n return null;\n }\n\n // Update access time for LRU\n entry.lastAccessTime = performance.now();\n\n // Round time to nearest 0.01s for cache hits (100 possible values per second)\n const roundedTime = Math.round(time * 100) / 100;\n return entry.keyframes.get(roundedTime) ?? null;\n }\n\n /**\n * Cache an interpolated keyframe\n *\n * @param animationId - Animation identifier\n * @param animation - Animation data\n * @param time - Current time\n * @param keyframe - Interpolated keyframe to cache\n */\n set(\n animationId: string,\n animation: SkeletalAnimation,\n time: number,\n keyframe: AnimationKeyframe,\n ): void {\n let entry = this.cache.get(animationId);\n\n if (!entry) {\n // Evict oldest entry if cache is full\n if (this.cache.size >= this.maxCacheSize) {\n this.evictLRU();\n }\n\n entry = {\n animation,\n keyframes: new Map(),\n lastAccessTime: performance.now(),\n };\n this.cache.set(animationId, entry);\n }\n\n // Round time for consistent cache keys\n const roundedTime = Math.round(time * 100) / 100;\n\n // Clone keyframe data for caching (avoid reference issues)\n const cachedRotations = new Map<string, THREE.Euler>();\n keyframe.boneRotations.forEach((rotation, boneName) => {\n cachedRotations.set(boneName, rotation.clone());\n });\n\n const cachedPositions = new Map<string, THREE.Vector3>();\n if (keyframe.bonePositions && keyframe.bonePositions.size > 0) {\n keyframe.bonePositions.forEach((position, boneName) => {\n cachedPositions.set(boneName, position.clone());\n });\n }\n\n entry.keyframes.set(roundedTime, {\n keyframe,\n timestamp: performance.now(),\n rotations: cachedRotations,\n positions: cachedPositions,\n });\n\n // Evict oldest keyframes if this animation has too many cached\n if (entry.keyframes.size > this.maxKeyframesPerAnimation) {\n this.evictOldestKeyframes(\n entry,\n entry.keyframes.size - this.maxKeyframesPerAnimation,\n );\n }\n }\n\n /**\n * Evict oldest keyframes from an animation cache entry\n * @param entry - Animation cache entry\n * @param count - Number of keyframes to evict\n */\n private evictOldestKeyframes(entry: AnimationCache, count: number): void {\n const keyframeTimes = Array.from(entry.keyframes.entries())\n .sort((a, b) => a[1].timestamp - b[1].timestamp)\n .slice(0, count)\n .map(([time]) => time);\n\n keyframeTimes.forEach((time) => entry.keyframes.delete(time));\n }\n\n /**\n * Evict least recently used cache entry\n */\n private evictLRU(): void {\n let oldestKey: string | null = null;\n let oldestTime = Infinity;\n\n this.cache.forEach((entry, key) => {\n if (entry.lastAccessTime < oldestTime) {\n oldestTime = entry.lastAccessTime;\n oldestKey = key;\n }\n });\n\n if (oldestKey) {\n this.cache.delete(oldestKey);\n }\n }\n\n /**\n * Clear all cached keyframes\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache statistics\n */\n getStats(): {\n totalAnimations: number;\n totalKeyframes: number;\n cacheSize: number;\n } {\n let totalKeyframes = 0;\n this.cache.forEach((entry) => {\n totalKeyframes += entry.keyframes.size;\n });\n\n return {\n totalAnimations: this.cache.size,\n totalKeyframes,\n cacheSize: this.maxCacheSize,\n };\n }\n}\n\n/**\n * Global animation cache instance\n *\n * @korean 전역애니메이션캐시\n */\nexport const animationCache = new AnimationCacheManager(50);\n\n/**\n * Batch update multiple bones with dirty flag optimization\n *\n * Only updates bones that have changed rotations/positions.\n * Uses object pooling to avoid allocations.\n *\n * @param rig - Skeletal rig to update\n * @param keyframe - Keyframe with bone transforms\n * @param dirtyBones - Set of bone names that changed (optional, updates all if not provided)\n *\n * @korean 배치뼈업데이트\n */\nexport function batchUpdateBones(\n rig: SkeletalRig,\n keyframe: AnimationKeyframe,\n dirtyBones?: Set<string>,\n): void {\n const bonesToUpdate = dirtyBones ?? new Set(keyframe.boneRotations.keys());\n\n bonesToUpdate.forEach((boneName) => {\n const bone = rig.bones.get(boneName);\n if (!bone) {\n return;\n }\n\n // Update rotation\n const rotation = keyframe.boneRotations.get(boneName);\n if (rotation) {\n bone.rotation.copy(rotation);\n }\n\n // Update position (if animated) - apply as offset from rest position\n const position = keyframe.bonePositions?.get(boneName);\n if (position) {\n // Position values in animations are offsets from rest position, not absolute values\n bone.position.copy(bone.restPosition).add(position);\n }\n });\n}\n\n/**\n * Precompute and cache animation interpolation\n *\n * Generates cached keyframes at regular intervals for smooth playback.\n * Call this during asset loading or idle time.\n *\n * @param animationId - Animation identifier\n * @param animation - Animation to precompute\n * @param sampleRate - Samples per second (default: 60fps = 60 samples/s)\n *\n * @korean 애니메이션사전계산\n */\nexport function precomputeAnimation(\n animationId: string,\n animation: SkeletalAnimation,\n sampleRate = 60,\n): void {\n const duration = animation.duration;\n const step = 1 / sampleRate;\n\n for (let t = 0; t <= duration; t += step) {\n // This will populate the cache\n const keyframe = interpolateKeyframeCached(animationId, animation, t);\n if (keyframe) {\n animationCache.set(animationId, animation, t, keyframe);\n }\n }\n}\n\n/**\n * Interpolate keyframe with caching\n *\n * Checks cache before performing interpolation.\n * Automatically caches result for future use.\n *\n * @param animationId - Animation identifier\n * @param animation - Animation data\n * @param time - Current time\n * @returns Interpolated keyframe\n *\n * @korean 캐시된키프레임보간\n */\nexport function interpolateKeyframeCached(\n animationId: string,\n animation: SkeletalAnimation,\n time: number,\n): AnimationKeyframe | null {\n // Check cache first\n const cached = animationCache.get(animationId, animation, time);\n if (cached) {\n return cached.keyframe;\n }\n\n // Find surrounding keyframes\n const keyframes = animation.keyframes;\n if (keyframes.length === 0) {\n return null;\n }\n\n // Find previous and next keyframes\n let prevKeyframe = keyframes[0];\n let nextKeyframe = keyframes[keyframes.length - 1];\n\n for (let i = 0; i < keyframes.length - 1; i++) {\n if (time >= keyframes[i].time && time <= keyframes[i + 1].time) {\n prevKeyframe = keyframes[i];\n nextKeyframe = keyframes[i + 1];\n break;\n }\n }\n\n // If at exact keyframe, return it directly\n if (Math.abs(time - prevKeyframe.time) < 0.001) {\n animationCache.set(animationId, animation, time, prevKeyframe);\n return prevKeyframe;\n }\n\n // Calculate interpolation factor\n const timeDiff = nextKeyframe.time - prevKeyframe.time;\n const t = timeDiff > 0 ? (time - prevKeyframe.time) / timeDiff : 0;\n\n // Interpolate rotations using object pool\n const interpolatedRotations = new Map<string, THREE.Euler>();\n\n prevKeyframe.boneRotations.forEach((prevRot, boneName) => {\n const nextRot = nextKeyframe.boneRotations.get(boneName);\n if (nextRot) {\n // Use pooled quaternions for slerp interpolation\n const prevQuat = ThreeObjectPools.quaternion.acquire();\n const nextQuat = ThreeObjectPools.quaternion.acquire();\n const resultQuat = ThreeObjectPools.quaternion.acquire();\n\n prevQuat.setFromEuler(prevRot);\n nextQuat.setFromEuler(nextRot);\n resultQuat.slerpQuaternions(prevQuat, nextQuat, t);\n\n // Use pooled Euler, then clone for storage to avoid pooled object reuse issues\n const tempEuler = ThreeObjectPools.euler.acquire();\n tempEuler.setFromQuaternion(resultQuat);\n interpolatedRotations.set(boneName, tempEuler.clone());\n\n // Release pooled objects\n ThreeObjectPools.quaternion.release(prevQuat);\n ThreeObjectPools.quaternion.release(nextQuat);\n ThreeObjectPools.quaternion.release(resultQuat);\n ThreeObjectPools.euler.release(tempEuler);\n } else {\n interpolatedRotations.set(boneName, prevRot.clone());\n }\n });\n\n // Interpolate positions if present\n const interpolatedPositions = new Map<string, THREE.Vector3>();\n if (\n prevKeyframe.bonePositions &&\n prevKeyframe.bonePositions.size > 0 &&\n nextKeyframe.bonePositions &&\n nextKeyframe.bonePositions.size > 0\n ) {\n prevKeyframe.bonePositions.forEach((prevPos, boneName) => {\n const nextPos = nextKeyframe.bonePositions?.get(boneName);\n if (nextPos) {\n // Use pooled Vector3, then clone for storage to avoid pooled object reuse issues\n const tempVec = ThreeObjectPools.vector3.acquire();\n tempVec.lerpVectors(prevPos, nextPos, t);\n interpolatedPositions.set(boneName, tempVec.clone());\n ThreeObjectPools.vector3.release(tempVec);\n }\n });\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // ANATOMY STATE INTERPOLATION (해부학 상태 보간)\n // ═══════════════════════════════════════════════════════════════════════════\n\n // For anatomy state, we use discrete values (no interpolation - nearest keyframe wins)\n // At t < 0.5 use prevKeyframe, at t >= 0.5 use nextKeyframe\n const useNextAnatomy = t >= 0.5;\n const anatomySource = useNextAnatomy ? nextKeyframe : prevKeyframe;\n\n // Interpolate muscle activations if present\n let interpolatedMuscles: Map<string, number> | undefined;\n if (prevKeyframe.muscleActivations || nextKeyframe.muscleActivations) {\n interpolatedMuscles = new Map();\n const prevMuscles = prevKeyframe.muscleActivations ?? new Map();\n const nextMuscles = nextKeyframe.muscleActivations ?? new Map();\n\n // Collect all muscle groups from both keyframes\n const allMuscles = new Set([...prevMuscles.keys(), ...nextMuscles.keys()]);\n allMuscles.forEach((muscle) => {\n const prevVal = prevMuscles.get(muscle) ?? 0;\n const nextVal = nextMuscles.get(muscle) ?? 0;\n // Linear interpolation for muscle tension\n interpolatedMuscles?.set(muscle, prevVal + (nextVal - prevVal) * t);\n });\n }\n\n const interpolatedKeyframe: AnimationKeyframe = {\n time,\n boneRotations: interpolatedRotations,\n bonePositions: interpolatedPositions,\n easing: prevKeyframe.easing,\n // Anatomy state (discrete, from nearest keyframe)\n leftHandPose: anatomySource.leftHandPose,\n rightHandPose: anatomySource.rightHandPose,\n leftHandHighlightMode: anatomySource.leftHandHighlightMode,\n rightHandHighlightMode: anatomySource.rightHandHighlightMode,\n leftFootHighlight: anatomySource.leftFootHighlight,\n rightFootHighlight: anatomySource.rightFootHighlight,\n facialExpression: anatomySource.facialExpression,\n // Muscle activations (interpolated)\n muscleActivations: interpolatedMuscles,\n };\n\n // Cache for future use\n animationCache.set(animationId, animation, time, interpolatedKeyframe);\n\n return interpolatedKeyframe;\n}\n\n/**\n * Batch transform multiple bones in a single operation\n *\n * Applies transformations to all bones efficiently using temporary objects.\n * Reduces per-bone overhead by batching operations.\n *\n * @param rig - Skeletal rig\n * @param transforms - Map of bone names to transforms\n *\n * @korean 배치변환\n */\nexport function batchTransformBones(\n rig: SkeletalRig,\n transforms: Map<string, { rotation?: THREE.Euler; position?: THREE.Vector3 }>,\n): void {\n transforms.forEach((transform, boneName) => {\n const bone = rig.bones.get(boneName);\n if (!bone) {\n return;\n }\n\n if (transform.rotation) {\n bone.rotation.copy(transform.rotation);\n }\n\n if (transform.position) {\n bone.position.copy(transform.position);\n }\n });\n}\n\n/**\n * Calculate dirty bones between two keyframes\n *\n * Identifies which bones have changed between keyframes for dirty flag optimization.\n * Only changed bones need to be updated.\n *\n * @param prevKeyframe - Previous keyframe\n * @param nextKeyframe - Next keyframe\n * @param threshold - Minimum rotation difference in radians (default: 0.01)\n * @returns Set of bone names that changed\n *\n * @korean 변경된뼈계산\n */\nexport function calculateDirtyBones(\n prevKeyframe: AnimationKeyframe,\n nextKeyframe: AnimationKeyframe,\n threshold = 0.01,\n): Set<string> {\n const dirtyBones = new Set<string>();\n\n nextKeyframe.boneRotations.forEach((nextRot, boneName) => {\n const prevRot = prevKeyframe.boneRotations.get(boneName);\n if (!prevRot) {\n dirtyBones.add(boneName);\n return;\n }\n\n // Check if rotation changed significantly\n const dx = Math.abs(nextRot.x - prevRot.x);\n const dy = Math.abs(nextRot.y - prevRot.y);\n const dz = Math.abs(nextRot.z - prevRot.z);\n\n if (dx > threshold || dy > threshold || dz > threshold) {\n dirtyBones.add(boneName);\n }\n });\n\n // Check positions if animated\n if (nextKeyframe.bonePositions && nextKeyframe.bonePositions.size > 0) {\n nextKeyframe.bonePositions.forEach((nextPos, boneName) => {\n const prevPos = prevKeyframe.bonePositions?.get(boneName);\n if (!prevPos) {\n dirtyBones.add(boneName);\n return;\n }\n\n // Check if position changed\n const distance = nextPos.distanceTo(prevPos);\n if (distance > threshold) {\n dirtyBones.add(boneName);\n }\n });\n }\n\n return dirtyBones;\n}\n\n/**\n * Animation performance metrics\n *\n * @korean 애니메이션성능지표\n */\nexport interface AnimationPerformanceMetrics {\n /** Average frame time (ms) */\n avgFrameTime: number;\n /** Maximum frame time (ms) */\n maxFrameTime: number;\n /** Minimum frame time (ms) */\n minFrameTime: number;\n /** Number of frames measured */\n frameCount: number;\n /** Cache hit rate (0-1) */\n cacheHitRate: number;\n /** Total cache entries */\n cacheEntries: number;\n}\n\n/**\n * Performance monitor for animation system\n *\n * @korean 성능모니터\n */\nclass AnimationPerformanceMonitor {\n private frameTimes: number[] = [];\n private cacheHits = 0;\n private cacheMisses = 0;\n private readonly maxSamples = 120; // 2 seconds at 60fps\n\n /**\n * Record frame time\n * @param time - Frame time in milliseconds\n */\n recordFrame(time: number): void {\n this.frameTimes.push(time);\n if (this.frameTimes.length > this.maxSamples) {\n this.frameTimes.shift();\n }\n }\n\n /**\n * Record cache hit\n */\n recordCacheHit(): void {\n this.cacheHits++;\n }\n\n /**\n * Record cache miss\n */\n recordCacheMiss(): void {\n this.cacheMisses++;\n }\n\n /**\n * Get current metrics\n */\n getMetrics(): AnimationPerformanceMetrics {\n const totalHits = this.cacheHits + this.cacheMisses;\n const cacheHitRate = totalHits > 0 ? this.cacheHits / totalHits : 0;\n\n const stats = animationCache.getStats();\n\n if (this.frameTimes.length === 0) {\n return {\n avgFrameTime: 0,\n maxFrameTime: 0,\n minFrameTime: 0,\n frameCount: 0,\n cacheHitRate,\n cacheEntries: stats.totalKeyframes,\n };\n }\n\n const avgFrameTime =\n this.frameTimes.reduce((a, b) => a + b, 0) / this.frameTimes.length;\n const maxFrameTime = Math.max(...this.frameTimes);\n const minFrameTime = Math.min(...this.frameTimes);\n\n return {\n avgFrameTime,\n maxFrameTime,\n minFrameTime,\n frameCount: this.frameTimes.length,\n cacheHitRate,\n cacheEntries: stats.totalKeyframes,\n };\n }\n\n /**\n * Reset metrics\n */\n reset(): void {\n this.frameTimes = [];\n this.cacheHits = 0;\n this.cacheMisses = 0;\n }\n}\n\n/**\n * Global performance monitor instance\n *\n * @korean 전역성능모니터\n */\nexport const performanceMonitor = new AnimationPerformanceMonitor();\n"],"mappings":";;;;;;;;;;AA+DA,IAAM,wBAAN,MAA4B;CAC1B,wBAAgB,IAAI,IAA4B;CAChD;CACA;CAEA,YAAY,eAAe,IAAI,2BAA2B,KAAK;EAC7D,KAAK,eAAe;EACpB,KAAK,2BAA2B;CAClC;;;;;;;;;CAUA,IACE,aACA,WACA,MACuB;EAGvB,MAAM,QAAQ,KAAK,MAAM,IAAI,WAAW;EACxC,IAAI,CAAC,OACH,OAAO;EAIT,MAAM,iBAAiB,YAAY,IAAI;EAGvC,MAAM,cAAc,KAAK,MAAM,OAAO,GAAG,IAAI;EAC7C,OAAO,MAAM,UAAU,IAAI,WAAW,KAAK;CAC7C;;;;;;;;;CAUA,IACE,aACA,WACA,MACA,UACM;EACN,IAAI,QAAQ,KAAK,MAAM,IAAI,WAAW;EAEtC,IAAI,CAAC,OAAO;GAEV,IAAI,KAAK,MAAM,QAAQ,KAAK,cAC1B,KAAK,SAAS;GAGhB,QAAQ;IACN;IACA,2BAAW,IAAI,IAAI;IACnB,gBAAgB,YAAY,IAAI;GAClC;GACA,KAAK,MAAM,IAAI,aAAa,KAAK;EACnC;EAGA,MAAM,cAAc,KAAK,MAAM,OAAO,GAAG,IAAI;EAG7C,MAAM,kCAAkB,IAAI,IAAyB;EACrD,SAAS,cAAc,SAAS,UAAU,aAAa;GACrD,gBAAgB,IAAI,UAAU,SAAS,MAAM,CAAC;EAChD,CAAC;EAED,MAAM,kCAAkB,IAAI,IAA2B;EACvD,IAAI,SAAS,iBAAiB,SAAS,cAAc,OAAO,GAC1D,SAAS,cAAc,SAAS,UAAU,aAAa;GACrD,gBAAgB,IAAI,UAAU,SAAS,MAAM,CAAC;EAChD,CAAC;EAGH,MAAM,UAAU,IAAI,aAAa;GAC/B;GACA,WAAW,YAAY,IAAI;GAC3B,WAAW;GACX,WAAW;EACb,CAAC;EAGD,IAAI,MAAM,UAAU,OAAO,KAAK,0BAC9B,KAAK,qBACH,OACA,MAAM,UAAU,OAAO,KAAK,wBAC9B;CAEJ;;;;;;CAOA,qBAA6B,OAAuB,OAAqB;EAMvE,MAL4B,KAAK,MAAM,UAAU,QAAQ,CAAC,EACvD,MAAM,GAAG,MAAM,EAAE,GAAG,YAAY,EAAE,GAAG,SAAS,EAC9C,MAAM,GAAG,KAAK,EACd,KAAK,CAAC,UAAU,IAEnB,EAAc,SAAS,SAAS,MAAM,UAAU,OAAO,IAAI,CAAC;CAC9D;;;;CAKA,WAAyB;EACvB,IAAI,YAA2B;EAC/B,IAAI,aAAa;EAEjB,KAAK,MAAM,SAAS,OAAO,QAAQ;GACjC,IAAI,MAAM,iBAAiB,YAAY;IACrC,aAAa,MAAM;IACnB,YAAY;GACd;EACF,CAAC;EAED,IAAI,WACF,KAAK,MAAM,OAAO,SAAS;CAE/B;;;;CAKA,QAAc;EACZ,KAAK,MAAM,MAAM;CACnB;;;;CAKA,WAIE;EACA,IAAI,iBAAiB;EACrB,KAAK,MAAM,SAAS,UAAU;GAC5B,kBAAkB,MAAM,UAAU;EACpC,CAAC;EAED,OAAO;GACL,iBAAiB,KAAK,MAAM;GAC5B;GACA,WAAW,KAAK;EAClB;CACF;AACF;;;;;;AAOA,IAAa,iBAAiB,IAAI,sBAAsB,EAAE;;;;;;;;;;;;;AAc1D,SAAgB,iBACd,KACA,UACA,YACM;CAGN,CAFsB,cAAc,IAAI,IAAI,SAAS,cAAc,KAAK,CAAC,GAE3D,SAAS,aAAa;EAClC,MAAM,OAAO,IAAI,MAAM,IAAI,QAAQ;EACnC,IAAI,CAAC,MACH;EAIF,MAAM,WAAW,SAAS,cAAc,IAAI,QAAQ;EACpD,IAAI,UACF,KAAK,SAAS,KAAK,QAAQ;EAI7B,MAAM,WAAW,SAAS,eAAe,IAAI,QAAQ;EACrD,IAAI,UAEF,KAAK,SAAS,KAAK,KAAK,YAAY,EAAE,IAAI,QAAQ;CAEtD,CAAC;AACH;;;;;;;;;;;;;AAcA,SAAgB,oBACd,aACA,WACA,aAAa,IACP;CACN,MAAM,WAAW,UAAU;CAC3B,MAAM,OAAO,IAAI;CAEjB,KAAK,IAAI,IAAI,GAAG,KAAK,UAAU,KAAK,MAAM;EAExC,MAAM,WAAW,0BAA0B,aAAa,WAAW,CAAC;EACpE,IAAI,UACF,eAAe,IAAI,aAAa,WAAW,GAAG,QAAQ;CAE1D;AACF;;;;;;;;;;;;;;AAeA,SAAgB,0BACd,aACA,WACA,MAC0B;CAE1B,MAAM,SAAS,eAAe,IAAI,aAAa,WAAW,IAAI;CAC9D,IAAI,QACF,OAAO,OAAO;CAIhB,MAAM,YAAY,UAAU;CAC5B,IAAI,UAAU,WAAW,GACvB,OAAO;CAIT,IAAI,eAAe,UAAU;CAC7B,IAAI,eAAe,UAAU,UAAU,SAAS;CAEhD,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,SAAS,GAAG,KACxC,IAAI,QAAQ,UAAU,GAAG,QAAQ,QAAQ,UAAU,IAAI,GAAG,MAAM;EAC9D,eAAe,UAAU;EACzB,eAAe,UAAU,IAAI;EAC7B;CACF;CAIF,IAAI,KAAK,IAAI,OAAO,aAAa,IAAI,IAAI,MAAO;EAC9C,eAAe,IAAI,aAAa,WAAW,MAAM,YAAY;EAC7D,OAAO;CACT;CAGA,MAAM,WAAW,aAAa,OAAO,aAAa;CAClD,MAAM,IAAI,WAAW,KAAK,OAAO,aAAa,QAAQ,WAAW;CAGjE,MAAM,wCAAwB,IAAI,IAAyB;CAE3D,aAAa,cAAc,SAAS,SAAS,aAAa;EACxD,MAAM,UAAU,aAAa,cAAc,IAAI,QAAQ;EACvD,IAAI,SAAS;GAEX,MAAM,WAAW,iBAAiB,WAAW,QAAQ;GACrD,MAAM,WAAW,iBAAiB,WAAW,QAAQ;GACrD,MAAM,aAAa,iBAAiB,WAAW,QAAQ;GAEvD,SAAS,aAAa,OAAO;GAC7B,SAAS,aAAa,OAAO;GAC7B,WAAW,iBAAiB,UAAU,UAAU,CAAC;GAGjD,MAAM,YAAY,iBAAiB,MAAM,QAAQ;GACjD,UAAU,kBAAkB,UAAU;GACtC,sBAAsB,IAAI,UAAU,UAAU,MAAM,CAAC;GAGrD,iBAAiB,WAAW,QAAQ,QAAQ;GAC5C,iBAAiB,WAAW,QAAQ,QAAQ;GAC5C,iBAAiB,WAAW,QAAQ,UAAU;GAC9C,iBAAiB,MAAM,QAAQ,SAAS;EAC1C,OACE,sBAAsB,IAAI,UAAU,QAAQ,MAAM,CAAC;CAEvD,CAAC;CAGD,MAAM,wCAAwB,IAAI,IAA2B;CAC7D,IACE,aAAa,iBACb,aAAa,cAAc,OAAO,KAClC,aAAa,iBACb,aAAa,cAAc,OAAO,GAElC,aAAa,cAAc,SAAS,SAAS,aAAa;EACxD,MAAM,UAAU,aAAa,eAAe,IAAI,QAAQ;EACxD,IAAI,SAAS;GAEX,MAAM,UAAU,iBAAiB,QAAQ,QAAQ;GACjD,QAAQ,YAAY,SAAS,SAAS,CAAC;GACvC,sBAAsB,IAAI,UAAU,QAAQ,MAAM,CAAC;GACnD,iBAAiB,QAAQ,QAAQ,OAAO;EAC1C;CACF,CAAC;CAUH,MAAM,gBADiB,KAAK,KACW,eAAe;CAGtD,IAAI;CACJ,IAAI,aAAa,qBAAqB,aAAa,mBAAmB;EACpE,sCAAsB,IAAI,IAAI;EAC9B,MAAM,cAAc,aAAa,qCAAqB,IAAI,IAAI;EAC9D,MAAM,cAAc,aAAa,qCAAqB,IAAI,IAAI;EAI9D,IADuB,IAAI,CAAC,GAAG,YAAY,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CACxE,EAAW,SAAS,WAAW;GAC7B,MAAM,UAAU,YAAY,IAAI,MAAM,KAAK;GAC3C,MAAM,UAAU,YAAY,IAAI,MAAM,KAAK;GAE3C,qBAAqB,IAAI,QAAQ,WAAW,UAAU,WAAW,CAAC;EACpE,CAAC;CACH;CAEA,MAAM,uBAA0C;EAC9C;EACA,eAAe;EACf,eAAe;EACf,QAAQ,aAAa;EAErB,cAAc,cAAc;EAC5B,eAAe,cAAc;EAC7B,uBAAuB,cAAc;EACrC,wBAAwB,cAAc;EACtC,mBAAmB,cAAc;EACjC,oBAAoB,cAAc;EAClC,kBAAkB,cAAc;EAEhC,mBAAmB;CACrB;CAGA,eAAe,IAAI,aAAa,WAAW,MAAM,oBAAoB;CAErE,OAAO;AACT;;;;;;AAmHA,IAAM,8BAAN,MAAkC;CAChC,aAA+B,CAAC;CAChC,YAAoB;CACpB,cAAsB;CACtB,aAA8B;;;;;CAM9B,YAAY,MAAoB;EAC9B,KAAK,WAAW,KAAK,IAAI;EACzB,IAAI,KAAK,WAAW,SAAS,KAAK,YAChC,KAAK,WAAW,MAAM;CAE1B;;;;CAKA,iBAAuB;EACrB,KAAK;CACP;;;;CAKA,kBAAwB;EACtB,KAAK;CACP;;;;CAKA,aAA0C;EACxC,MAAM,YAAY,KAAK,YAAY,KAAK;EACxC,MAAM,eAAe,YAAY,IAAI,KAAK,YAAY,YAAY;EAElE,MAAM,QAAQ,eAAe,SAAS;EAEtC,IAAI,KAAK,WAAW,WAAW,GAC7B,OAAO;GACL,cAAc;GACd,cAAc;GACd,cAAc;GACd,YAAY;GACZ;GACA,cAAc,MAAM;EACtB;EAQF,OAAO;GACL,cALA,KAAK,WAAW,QAAQ,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW;GAM7D,cALmB,KAAK,IAAI,GAAG,KAAK,UAKpC;GACA,cALmB,KAAK,IAAI,GAAG,KAAK,UAKpC;GACA,YAAY,KAAK,WAAW;GAC5B;GACA,cAAc,MAAM;EACtB;CACF;;;;CAKA,QAAc;EACZ,KAAK,aAAa,CAAC;EACnB,KAAK,YAAY;EACjB,KAAK,cAAc;CACrB;AACF;;;;;;AAOA,IAAa,qBAAqB,IAAI,4BAA4B"}
1
+ {"version":3,"file":"AnimationOptimizations.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationOptimizations.ts"],"sourcesContent":["/**\n * Optimized Animation System with Caching and Batch Processing\n *\n * High-performance animation pipeline optimizations:\n * - Keyframe caching to avoid redundant interpolations\n * - Batch bone transformation updates\n * - Dirty flag optimization\n * - Precomputed interpolation curves\n *\n * Target: Reduce animation overhead from ~8ms to <5ms per frame\n *\n * @module systems/animation/AnimationOptimizations\n * @category Animation System\n * @korean 애니메이션최적화\n */\n\nimport type {\n AnimationKeyframe,\n SkeletalAnimation,\n SkeletalRig,\n} from \"@/types/skeletal\";\nimport { ThreeObjectPools } from \"@/utils/threeObjectPool\";\nimport * as THREE from \"three\";\n\n/**\n * Cached keyframe with interpolated values\n * Reduces redundant interpolation calculations\n *\n * @korean 캐시된키프레임\n */\ninterface CachedKeyframe {\n /** Original keyframe */\n readonly keyframe: AnimationKeyframe;\n /** Cache timestamp (for invalidation) */\n readonly timestamp: number;\n /** Interpolated bone rotations (Map to avoid allocations) */\n readonly rotations: Map<string, THREE.Euler>;\n /** Interpolated bone positions (if animated) */\n readonly positions: Map<string, THREE.Vector3>;\n}\n\n/**\n * Animation cache entry\n *\n * @korean 애니메이션캐시항목\n */\ninterface AnimationCache {\n /** Animation being cached */\n readonly animation: SkeletalAnimation;\n /** Cached keyframes by time */\n readonly keyframes: Map<number, CachedKeyframe>;\n /** Last access time (for LRU eviction) */\n lastAccessTime: number;\n}\n\n/**\n * Animation Cache Manager\n *\n * Caches interpolated keyframes to avoid redundant calculations.\n * Uses LRU eviction when cache is full.\n *\n * @korean 애니메이션캐시관리자\n */\nclass AnimationCacheManager {\n private cache = new Map<string, AnimationCache>();\n private readonly maxCacheSize: number;\n private readonly maxKeyframesPerAnimation: number;\n\n constructor(maxCacheSize = 50, maxKeyframesPerAnimation = 200) {\n this.maxCacheSize = maxCacheSize;\n this.maxKeyframesPerAnimation = maxKeyframesPerAnimation;\n }\n\n /**\n * Get cached keyframe or create new entry\n *\n * @param animationId - Animation identifier\n * @param animation - Animation data (reserved for future cache invalidation logic)\n * @param time - Current time\n * @returns Cached keyframe or null if not cached\n */\n get(\n animationId: string,\n animation: SkeletalAnimation,\n time: number,\n ): CachedKeyframe | null {\n // Mark parameter as used for future extensibility (e.g., cache invalidation)\n void animation;\n const entry = this.cache.get(animationId);\n if (!entry) {\n return null;\n }\n\n // Update access time for LRU\n entry.lastAccessTime = performance.now();\n\n // Round time to nearest 0.01s for cache hits (100 possible values per second)\n const roundedTime = Math.round(time * 100) / 100;\n return entry.keyframes.get(roundedTime) ?? null;\n }\n\n /**\n * Cache an interpolated keyframe\n *\n * @param animationId - Animation identifier\n * @param animation - Animation data\n * @param time - Current time\n * @param keyframe - Interpolated keyframe to cache\n */\n set(\n animationId: string,\n animation: SkeletalAnimation,\n time: number,\n keyframe: AnimationKeyframe,\n ): void {\n let entry = this.cache.get(animationId);\n\n if (!entry) {\n // Evict oldest entry if cache is full\n if (this.cache.size >= this.maxCacheSize) {\n this.evictLRU();\n }\n\n entry = {\n animation,\n keyframes: new Map(),\n lastAccessTime: performance.now(),\n };\n this.cache.set(animationId, entry);\n }\n\n // Round time for consistent cache keys\n const roundedTime = Math.round(time * 100) / 100;\n\n // Clone keyframe data for caching (avoid reference issues)\n const cachedRotations = new Map<string, THREE.Euler>();\n keyframe.boneRotations.forEach((rotation, boneName) => {\n cachedRotations.set(boneName, rotation.clone());\n });\n\n const cachedPositions = new Map<string, THREE.Vector3>();\n if (keyframe.bonePositions && keyframe.bonePositions.size > 0) {\n keyframe.bonePositions.forEach((position, boneName) => {\n cachedPositions.set(boneName, position.clone());\n });\n }\n\n entry.keyframes.set(roundedTime, {\n keyframe,\n timestamp: performance.now(),\n rotations: cachedRotations,\n positions: cachedPositions,\n });\n\n // Evict oldest keyframes if this animation has too many cached\n if (entry.keyframes.size > this.maxKeyframesPerAnimation) {\n this.evictOldestKeyframes(\n entry,\n entry.keyframes.size - this.maxKeyframesPerAnimation,\n );\n }\n }\n\n /**\n * Evict oldest keyframes from an animation cache entry\n * @param entry - Animation cache entry\n * @param count - Number of keyframes to evict\n */\n private evictOldestKeyframes(entry: AnimationCache, count: number): void {\n const keyframeTimes = Array.from(entry.keyframes.entries())\n .sort((a, b) => a[1].timestamp - b[1].timestamp)\n .slice(0, count)\n .map(([time]) => time);\n\n keyframeTimes.forEach((time) => entry.keyframes.delete(time));\n }\n\n /**\n * Evict least recently used cache entry\n */\n private evictLRU(): void {\n let oldestKey: string | null = null;\n let oldestTime = Infinity;\n\n this.cache.forEach((entry, key) => {\n if (entry.lastAccessTime < oldestTime) {\n oldestTime = entry.lastAccessTime;\n oldestKey = key;\n }\n });\n\n if (oldestKey) {\n this.cache.delete(oldestKey);\n }\n }\n\n /**\n * Clear all cached keyframes\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache statistics\n */\n getStats(): {\n totalAnimations: number;\n totalKeyframes: number;\n cacheSize: number;\n } {\n let totalKeyframes = 0;\n this.cache.forEach((entry) => {\n totalKeyframes += entry.keyframes.size;\n });\n\n return {\n totalAnimations: this.cache.size,\n totalKeyframes,\n cacheSize: this.maxCacheSize,\n };\n }\n}\n\n/**\n * Global animation cache instance\n *\n * @korean 전역애니메이션캐시\n */\nexport const animationCache = new AnimationCacheManager(50);\n\n/**\n * Batch update multiple bones with dirty flag optimization\n *\n * Only updates bones that have changed rotations/positions.\n * Uses object pooling to avoid allocations.\n *\n * @param rig - Skeletal rig to update\n * @param keyframe - Keyframe with bone transforms\n * @param dirtyBones - Set of bone names that changed (optional, updates all if not provided)\n *\n * @korean 배치뼈업데이트\n */\nexport function batchUpdateBones(\n rig: SkeletalRig,\n keyframe: AnimationKeyframe,\n dirtyBones?: Set<string>,\n): void {\n const bonesToUpdate = dirtyBones ?? new Set(keyframe.boneRotations.keys());\n\n bonesToUpdate.forEach((boneName) => {\n const bone = rig.bones.get(boneName);\n if (!bone) {\n return;\n }\n\n // Update rotation\n const rotation = keyframe.boneRotations.get(boneName);\n if (rotation) {\n bone.rotation.copy(rotation);\n }\n\n // Update position (if animated) - apply as offset from rest position\n const position = keyframe.bonePositions?.get(boneName);\n if (position) {\n // Position values in animations are offsets from rest position, not absolute values\n bone.position.copy(bone.restPosition).add(position);\n }\n });\n}\n\n/**\n * Precompute and cache animation interpolation\n *\n * Generates cached keyframes at regular intervals for smooth playback.\n * Call this during asset loading or idle time.\n *\n * @param animationId - Animation identifier\n * @param animation - Animation to precompute\n * @param sampleRate - Samples per second (default: 60fps = 60 samples/s)\n *\n * @korean 애니메이션사전계산\n */\nexport function precomputeAnimation(\n animationId: string,\n animation: SkeletalAnimation,\n sampleRate = 60,\n): void {\n const duration = animation.duration;\n const step = 1 / sampleRate;\n\n for (let t = 0; t <= duration; t += step) {\n // This will populate the cache\n const keyframe = interpolateKeyframeCached(animationId, animation, t);\n if (keyframe) {\n animationCache.set(animationId, animation, t, keyframe);\n }\n }\n}\n\n/**\n * Interpolate keyframe with caching\n *\n * Checks cache before performing interpolation.\n * Automatically caches result for future use.\n *\n * @param animationId - Animation identifier\n * @param animation - Animation data\n * @param time - Current time\n * @returns Interpolated keyframe\n *\n * @korean 캐시된키프레임보간\n */\nexport function interpolateKeyframeCached(\n animationId: string,\n animation: SkeletalAnimation,\n time: number,\n): AnimationKeyframe | null {\n // Check cache first\n const cached = animationCache.get(animationId, animation, time);\n if (cached) {\n return cached.keyframe;\n }\n\n // Find surrounding keyframes\n const keyframes = animation.keyframes;\n if (keyframes.length === 0) {\n return null;\n }\n\n // Find previous and next keyframes\n let prevKeyframe = keyframes[0];\n let nextKeyframe = keyframes[keyframes.length - 1];\n\n for (let i = 0; i < keyframes.length - 1; i++) {\n if (time >= keyframes[i].time && time <= keyframes[i + 1].time) {\n prevKeyframe = keyframes[i];\n nextKeyframe = keyframes[i + 1];\n break;\n }\n }\n\n // If at exact keyframe, return it directly\n if (Math.abs(time - prevKeyframe.time) < 0.001) {\n animationCache.set(animationId, animation, time, prevKeyframe);\n return prevKeyframe;\n }\n\n // Calculate interpolation factor\n const timeDiff = nextKeyframe.time - prevKeyframe.time;\n const t = timeDiff > 0 ? (time - prevKeyframe.time) / timeDiff : 0;\n\n // Interpolate rotations using object pool\n const interpolatedRotations = new Map<string, THREE.Euler>();\n\n prevKeyframe.boneRotations.forEach((prevRot, boneName) => {\n const nextRot = nextKeyframe.boneRotations.get(boneName);\n if (nextRot) {\n // Use pooled quaternions for slerp interpolation\n const prevQuat = ThreeObjectPools.quaternion.acquire();\n const nextQuat = ThreeObjectPools.quaternion.acquire();\n const resultQuat = ThreeObjectPools.quaternion.acquire();\n\n prevQuat.setFromEuler(prevRot);\n nextQuat.setFromEuler(nextRot);\n resultQuat.slerpQuaternions(prevQuat, nextQuat, t);\n\n // Use pooled Euler, then clone for storage to avoid pooled object reuse issues\n const tempEuler = ThreeObjectPools.euler.acquire();\n tempEuler.setFromQuaternion(resultQuat);\n interpolatedRotations.set(boneName, tempEuler.clone());\n\n // Release pooled objects\n ThreeObjectPools.quaternion.release(prevQuat);\n ThreeObjectPools.quaternion.release(nextQuat);\n ThreeObjectPools.quaternion.release(resultQuat);\n ThreeObjectPools.euler.release(tempEuler);\n } else {\n interpolatedRotations.set(boneName, prevRot.clone());\n }\n });\n\n // Interpolate positions if present\n const interpolatedPositions = new Map<string, THREE.Vector3>();\n if (\n prevKeyframe.bonePositions &&\n prevKeyframe.bonePositions.size > 0 &&\n nextKeyframe.bonePositions &&\n nextKeyframe.bonePositions.size > 0\n ) {\n prevKeyframe.bonePositions.forEach((prevPos, boneName) => {\n const nextPos = nextKeyframe.bonePositions?.get(boneName);\n if (nextPos) {\n // Use pooled Vector3, then clone for storage to avoid pooled object reuse issues\n const tempVec = ThreeObjectPools.vector3.acquire();\n tempVec.lerpVectors(prevPos, nextPos, t);\n interpolatedPositions.set(boneName, tempVec.clone());\n ThreeObjectPools.vector3.release(tempVec);\n }\n });\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // ANATOMY STATE INTERPOLATION (해부학 상태 보간)\n // ═══════════════════════════════════════════════════════════════════════════\n\n // For anatomy state, we use discrete values (no interpolation - nearest keyframe wins)\n // At t < 0.5 use prevKeyframe, at t >= 0.5 use nextKeyframe\n const useNextAnatomy = t >= 0.5;\n const anatomySource = useNextAnatomy ? nextKeyframe : prevKeyframe;\n\n // Interpolate muscle activations if present\n let interpolatedMuscles: Map<string, number> | undefined;\n if (prevKeyframe.muscleActivations || nextKeyframe.muscleActivations) {\n interpolatedMuscles = new Map();\n const prevMuscles = prevKeyframe.muscleActivations ?? new Map();\n const nextMuscles = nextKeyframe.muscleActivations ?? new Map();\n\n // Collect all muscle groups from both keyframes\n const allMuscles = new Set([...prevMuscles.keys(), ...nextMuscles.keys()]);\n allMuscles.forEach((muscle) => {\n const prevVal = prevMuscles.get(muscle) ?? 0;\n const nextVal = nextMuscles.get(muscle) ?? 0;\n // Linear interpolation for muscle tension\n interpolatedMuscles?.set(muscle, prevVal + (nextVal - prevVal) * t);\n });\n }\n\n const interpolatedKeyframe: AnimationKeyframe = {\n time,\n boneRotations: interpolatedRotations,\n bonePositions: interpolatedPositions,\n easing: prevKeyframe.easing,\n // Anatomy state (discrete, from nearest keyframe)\n leftHandPose: anatomySource.leftHandPose,\n rightHandPose: anatomySource.rightHandPose,\n leftHandHighlightMode: anatomySource.leftHandHighlightMode,\n rightHandHighlightMode: anatomySource.rightHandHighlightMode,\n leftFootHighlight: anatomySource.leftFootHighlight,\n rightFootHighlight: anatomySource.rightFootHighlight,\n facialExpression: anatomySource.facialExpression,\n // Muscle activations (interpolated)\n muscleActivations: interpolatedMuscles,\n };\n\n // Cache for future use\n animationCache.set(animationId, animation, time, interpolatedKeyframe);\n\n return interpolatedKeyframe;\n}\n\n/**\n * Batch transform multiple bones in a single operation\n *\n * Applies transformations to all bones efficiently using temporary objects.\n * Reduces per-bone overhead by batching operations.\n *\n * @param rig - Skeletal rig\n * @param transforms - Map of bone names to transforms\n *\n * @korean 배치변환\n */\nexport function batchTransformBones(\n rig: SkeletalRig,\n transforms: Map<string, { rotation?: THREE.Euler; position?: THREE.Vector3 }>,\n): void {\n transforms.forEach((transform, boneName) => {\n const bone = rig.bones.get(boneName);\n if (!bone) {\n return;\n }\n\n if (transform.rotation) {\n bone.rotation.copy(transform.rotation);\n }\n\n if (transform.position) {\n bone.position.copy(transform.position);\n }\n });\n}\n\n/**\n * Calculate dirty bones between two keyframes\n *\n * Identifies which bones have changed between keyframes for dirty flag optimization.\n * Only changed bones need to be updated.\n *\n * @param prevKeyframe - Previous keyframe\n * @param nextKeyframe - Next keyframe\n * @param threshold - Minimum rotation difference in radians (default: 0.01)\n * @returns Set of bone names that changed\n *\n * @korean 변경된뼈계산\n */\nexport function calculateDirtyBones(\n prevKeyframe: AnimationKeyframe,\n nextKeyframe: AnimationKeyframe,\n threshold = 0.01,\n): Set<string> {\n const dirtyBones = new Set<string>();\n\n nextKeyframe.boneRotations.forEach((nextRot, boneName) => {\n const prevRot = prevKeyframe.boneRotations.get(boneName);\n if (!prevRot) {\n dirtyBones.add(boneName);\n return;\n }\n\n // Check if rotation changed significantly\n const dx = Math.abs(nextRot.x - prevRot.x);\n const dy = Math.abs(nextRot.y - prevRot.y);\n const dz = Math.abs(nextRot.z - prevRot.z);\n\n if (dx > threshold || dy > threshold || dz > threshold) {\n dirtyBones.add(boneName);\n }\n });\n\n // Check positions if animated\n if (nextKeyframe.bonePositions && nextKeyframe.bonePositions.size > 0) {\n nextKeyframe.bonePositions.forEach((nextPos, boneName) => {\n const prevPos = prevKeyframe.bonePositions?.get(boneName);\n if (!prevPos) {\n dirtyBones.add(boneName);\n return;\n }\n\n // Check if position changed\n const distance = nextPos.distanceTo(prevPos);\n if (distance > threshold) {\n dirtyBones.add(boneName);\n }\n });\n }\n\n return dirtyBones;\n}\n\n/**\n * Animation performance metrics\n *\n * @korean 애니메이션성능지표\n */\nexport interface AnimationPerformanceMetrics {\n /** Average frame time (ms) */\n avgFrameTime: number;\n /** Maximum frame time (ms) */\n maxFrameTime: number;\n /** Minimum frame time (ms) */\n minFrameTime: number;\n /** Number of frames measured */\n frameCount: number;\n /** Cache hit rate (0-1) */\n cacheHitRate: number;\n /** Total cache entries */\n cacheEntries: number;\n}\n\n/**\n * Performance monitor for animation system\n *\n * @korean 성능모니터\n */\nclass AnimationPerformanceMonitor {\n private frameTimes: number[] = [];\n private cacheHits = 0;\n private cacheMisses = 0;\n private readonly maxSamples = 120; // 2 seconds at 60fps\n\n /**\n * Record frame time\n * @param time - Frame time in milliseconds\n */\n recordFrame(time: number): void {\n this.frameTimes.push(time);\n if (this.frameTimes.length > this.maxSamples) {\n this.frameTimes.shift();\n }\n }\n\n /**\n * Record cache hit\n */\n recordCacheHit(): void {\n this.cacheHits++;\n }\n\n /**\n * Record cache miss\n */\n recordCacheMiss(): void {\n this.cacheMisses++;\n }\n\n /**\n * Get current metrics\n */\n getMetrics(): AnimationPerformanceMetrics {\n const totalHits = this.cacheHits + this.cacheMisses;\n const cacheHitRate = totalHits > 0 ? this.cacheHits / totalHits : 0;\n\n const stats = animationCache.getStats();\n\n if (this.frameTimes.length === 0) {\n return {\n avgFrameTime: 0,\n maxFrameTime: 0,\n minFrameTime: 0,\n frameCount: 0,\n cacheHitRate,\n cacheEntries: stats.totalKeyframes,\n };\n }\n\n const avgFrameTime =\n this.frameTimes.reduce((a, b) => a + b, 0) / this.frameTimes.length;\n const maxFrameTime = Math.max(...this.frameTimes);\n const minFrameTime = Math.min(...this.frameTimes);\n\n return {\n avgFrameTime,\n maxFrameTime,\n minFrameTime,\n frameCount: this.frameTimes.length,\n cacheHitRate,\n cacheEntries: stats.totalKeyframes,\n };\n }\n\n /**\n * Reset metrics\n */\n reset(): void {\n this.frameTimes = [];\n this.cacheHits = 0;\n this.cacheMisses = 0;\n }\n}\n\n/**\n * Global performance monitor instance\n *\n * @korean 전역성능모니터\n */\nexport const performanceMonitor = new AnimationPerformanceMonitor();\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AnimationPriority.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationPriority.ts"],"sourcesContent":["/**\n * Animation priority system for Black Trigram\n * \n * Determines which animations can interrupt others based on priority levels.\n * Higher priority animations can interrupt lower priority ones.\n * \n * Priority order: recovery > fall > ko > hit > attack > defend > stance_change > movement > idle\n * \n * Enhanced with:\n * - Deterministic conflict resolution for simultaneous equal-priority animations\n * - Animation queue system for pending animations\n * - Interruptibility windows (frame-based interrupt control)\n * - Korean terminology for all priority levels and conflicts\n * \n * @module systems/animation/AnimationPriority\n * @category Animation\n * @korean 애니메이션우선순위\n */\n\nimport { AnimationPriority, AnimationState, STEP_PRIORITY } from \"./types\";\n\n/**\n * Map animation states to their priority levels\n * \n * Stance guard animations have same priority as idle (0) since they are\n * also idle states, just stance-specific.\n * Tactical steps have priority 5 (same as attacks) to ensure commitment.\n * \n * @korean 애니메이션우선순위맵\n */\nexport const ANIMATION_PRIORITY_MAP: Record<AnimationState, AnimationPriority> = {\n idle: AnimationPriority.IDLE,\n walk: AnimationPriority.WALK,\n run: AnimationPriority.RUN,\n stance_change: AnimationPriority.STANCE_CHANGE,\n stance_side_switch: AnimationPriority.STANCE_CHANGE, // Same priority as stance_change\n defend: AnimationPriority.DEFEND,\n // Defensive animations (방어 애니메이션)\n defend_block_success: AnimationPriority.HIT, // Priority 6 - same as hit\n defend_parry: AnimationPriority.KO, // Priority 7 - higher than block\n defend_guard_break: AnimationPriority.FALL, // Priority 8 - highest (same as fall)\n defend_recovery: AnimationPriority.RUN, // Priority 2 - interruptible recovery\n attack: AnimationPriority.ATTACK,\n hit: AnimationPriority.HIT,\n ko: AnimationPriority.KO,\n // Stance-specific guard animations (팔괘 방어 자세)\n stance_guard_geon: AnimationPriority.IDLE,\n stance_guard_tae: AnimationPriority.IDLE,\n stance_guard_li: AnimationPriority.IDLE,\n stance_guard_jin: AnimationPriority.IDLE,\n stance_guard_son: AnimationPriority.IDLE,\n stance_guard_gam: AnimationPriority.IDLE,\n stance_guard_gan: AnimationPriority.IDLE,\n stance_guard_gon: AnimationPriority.IDLE,\n // Tactical step animations (전술적 발걸음) - non-interruptible\n step_forward: STEP_PRIORITY,\n step_back: STEP_PRIORITY,\n step_left: STEP_PRIORITY,\n step_right: STEP_PRIORITY,\n step_forward_left: STEP_PRIORITY,\n step_forward_right: STEP_PRIORITY,\n step_back_left: STEP_PRIORITY,\n step_back_right: STEP_PRIORITY,\n // Fall animations (낙법) - highest priority\n fall_forward: AnimationPriority.FALL,\n fall_backward: AnimationPriority.FALL,\n fall_side_left: AnimationPriority.FALL,\n fall_side_right: AnimationPriority.FALL,\n // Ground states (지면 자세) - idle priority\n ground_prone: AnimationPriority.IDLE,\n ground_supine: AnimationPriority.IDLE,\n ground_side_left: AnimationPriority.IDLE,\n ground_side_right: AnimationPriority.IDLE,\n // 180-degree turn animations (180도 회전) - same as steps (committed action)\n turn_left: STEP_PRIORITY,\n turn_right: STEP_PRIORITY,\n // Footwork patterns (보법) - Korean martial arts specialized footwork\n footwork_circular_left: STEP_PRIORITY,\n footwork_circular_right: STEP_PRIORITY,\n footwork_pivot_left: STEP_PRIORITY,\n footwork_pivot_right: STEP_PRIORITY,\n footwork_slide_forward: AnimationPriority.DEFEND,\n footwork_slide_back: AnimationPriority.DEFEND,\n footwork_slide_left: AnimationPriority.DEFEND,\n footwork_slide_right: AnimationPriority.DEFEND,\n footwork_shuffle: AnimationPriority.STANCE_CHANGE,\n // Recovery animations (기상 애니메이션) - highest priority (can interrupt anything)\n recovery_prone_standup: AnimationPriority.RECOVERY,\n recovery_supine_standup: AnimationPriority.RECOVERY,\n recovery_roll: AnimationPriority.RECOVERY,\n recovery_defensive: AnimationPriority.RECOVERY,\n // Grappling animations (잡기 애니메이션) - high priority like attacks/hits\n grapple_entry: AnimationPriority.ATTACK, // Priority 5 - same as attack\n grapple_control: AnimationPriority.ATTACK, // Priority 5 - maintain control\n grapple_struggle: AnimationPriority.HIT, // Priority 6 - struggling escape\n grapple_escape: AnimationPriority.HIT, // Priority 6 - successful escape\n};\n\n/**\n * Check if an animation can interrupt another based on priority\n * \n * @param current - Current animation state\n * @param requested - Requested animation state\n * @param currentInterruptible - Whether current animation is interruptible\n * @returns Whether the requested animation can interrupt the current one\n * \n * @example\n * ```typescript\n * // Hit can interrupt attack\n * canInterrupt(\"attack\", \"hit\", true); // true\n * \n * // Attack cannot interrupt hit\n * canInterrupt(\"hit\", \"attack\", true); // false\n * \n * // Nothing can interrupt non-interruptible animations\n * canInterrupt(\"attack\", \"hit\", false); // false (unless same priority)\n * ```\n * \n * @korean 중단가능여부확인\n */\nexport function canInterrupt(\n current: AnimationState,\n requested: AnimationState,\n currentInterruptible: boolean\n): boolean {\n const currentPriority = ANIMATION_PRIORITY_MAP[current];\n const requestedPriority = ANIMATION_PRIORITY_MAP[requested];\n\n // Same priority animations can always transition\n if (currentPriority === requestedPriority) {\n return true;\n }\n\n // Non-interruptible animations can only be interrupted by higher priority\n if (!currentInterruptible) {\n return requestedPriority > currentPriority;\n }\n\n // Interruptible animations can be interrupted by same or higher priority\n return requestedPriority >= currentPriority;\n}\n\n/**\n * Get the priority level for an animation state\n * \n * @param state - Animation state\n * @returns Priority level\n * \n * @korean 우선순위가져오기\n */\nexport function getPriority(state: AnimationState): AnimationPriority {\n return ANIMATION_PRIORITY_MAP[state];\n}\n\n/**\n * Compare two animation priorities\n * \n * @param state1 - First animation state\n * @param state2 - Second animation state\n * @returns Positive if state1 has higher priority, negative if state2 has higher priority, 0 if equal\n * \n * @korean 우선순위비교\n */\nexport function comparePriority(\n state1: AnimationState,\n state2: AnimationState\n): number {\n return ANIMATION_PRIORITY_MAP[state1] - ANIMATION_PRIORITY_MAP[state2];\n}\n\n// ===== Korean Priority Level Terminology (우선순위 등급 용어) =====\n\n/**\n * Korean names for each priority level\n * \n * Maps priority levels to their Korean martial arts terminology:\n * - IDLE (0): 대기 (Daegi) - Waiting/Ready state\n * - WALK (1): 보행 (Bohaeng) - Walking movement\n * - RUN (2): 질주 (Jilju) - Running movement\n * - STANCE_CHANGE (3): 자세전환 (Jase Jeonhwan) - Stance transition\n * - DEFEND (4): 방어 (Bangeo) - Defense action\n * - ATTACK (5): 공격 (Gonggyeok) - Attack action\n * - HIT (6): 피격 (Pigyeok) - Being hit\n * - KO (7): 기절 (Gijeol) - Knockout\n * - FALL (8): 낙법 (Nakbeop) - Falling technique\n * - RECOVERY (9): 기상 (Gisang) - Recovery/Getting up\n * \n * @korean 우선순위한글용어\n */\nexport const PRIORITY_LEVEL_KOREAN_NAMES: Record<AnimationPriority, {\n korean: string;\n romanized: string;\n english: string;\n}> = {\n [AnimationPriority.IDLE]: {\n korean: \"대기\",\n romanized: \"Daegi\",\n english: \"Ready State\",\n },\n [AnimationPriority.WALK]: {\n korean: \"보행\",\n romanized: \"Bohaeng\",\n english: \"Walking\",\n },\n [AnimationPriority.RUN]: {\n korean: \"질주\",\n romanized: \"Jilju\",\n english: \"Running\",\n },\n [AnimationPriority.STANCE_CHANGE]: {\n korean: \"자세전환\",\n romanized: \"Jase Jeonhwan\",\n english: \"Stance Change\",\n },\n [AnimationPriority.DEFEND]: {\n korean: \"방어\",\n romanized: \"Bangeo\",\n english: \"Defense\",\n },\n [AnimationPriority.ATTACK]: {\n korean: \"공격\",\n romanized: \"Gonggyeok\",\n english: \"Attack\",\n },\n [AnimationPriority.HIT]: {\n korean: \"피격\",\n romanized: \"Pigyeok\",\n english: \"Hit\",\n },\n [AnimationPriority.KO]: {\n korean: \"기절\",\n romanized: \"Gijeol\",\n english: \"Knockout\",\n },\n [AnimationPriority.FALL]: {\n korean: \"낙법\",\n romanized: \"Nakbeop\",\n english: \"Falling\",\n },\n [AnimationPriority.RECOVERY]: {\n korean: \"기상\",\n romanized: \"Gisang\",\n english: \"Recovery\",\n },\n};\n\n/**\n * Get Korean name for a priority level\n * \n * @param priority - Animation priority level\n * @returns Korean terminology object\n * \n * @korean 우선순위한글이름가져오기\n */\nexport function getPriorityKoreanName(priority: AnimationPriority): {\n korean: string;\n romanized: string;\n english: string;\n} {\n return PRIORITY_LEVEL_KOREAN_NAMES[priority];\n}\n\n// ===== Conflict Resolution (충돌 해결) =====\n\n/**\n * Conflict resolution strategy for equal-priority animations\n * \n * **Korean**: 충돌 해결 전략\n * \n * Determines how to handle when two animations of equal priority\n * are requested simultaneously:\n * \n * - `timestamp`: First animation requested wins (FIFO - 선입선출)\n * - `state_order`: Use AnimationState enum order as tiebreaker\n * - `current`: Keep current animation running\n * - `requested`: Always switch to requested animation\n * \n * @korean 충돌해결전략\n */\nexport type ConflictResolutionStrategy =\n | \"timestamp\" // 시간순서 (First requested wins)\n | \"state_order\" // 상태순서 (Enum order tiebreaker)\n | \"current\" // 현재유지 (Keep current)\n | \"requested\"; // 요청수락 (Accept requested)\n\n/**\n * Animation request with timestamp for conflict resolution\n * \n * **Korean**: 애니메이션 요청\n * \n * Tracks animation requests with timestamps to enable deterministic\n * conflict resolution when multiple equal-priority animations compete.\n * \n * @korean 애니메이션요청\n */\nexport interface AnimationRequest {\n /** Requested animation state */\n readonly state: AnimationState;\n /** Request timestamp in milliseconds (from performance.now()) */\n readonly timestamp: number;\n /** Request priority */\n readonly priority: AnimationPriority;\n /** \n * Whether this is a forced request (wins conflict resolution against non-forced requests)\n * \n * Note: This only affects conflict resolution between equal-priority animations in the queue.\n * It does not bypass priority checks or interrupt rules for the initial transition.\n * \n * @korean 강제요청\n */\n readonly forced?: boolean;\n}\n\n/**\n * Resolve conflict between two equal-priority animations\n * \n * **Korean**: 충돌 해결\n * \n * Uses the specified strategy to deterministically decide which animation\n * should play when both have equal priority.\n * \n * @param current - Current animation request\n * @param requested - Requested animation request\n * @param strategy - Conflict resolution strategy\n * @returns Which animation should play: 'current' or 'requested'\n * \n * @example\n * ```typescript\n * const current = {\n * state: AnimationState.ATTACK,\n * timestamp: 1000,\n * priority: AnimationPriority.ATTACK,\n * };\n * const requested = {\n * state: AnimationState.STEP_FORWARD,\n * timestamp: 1001,\n * priority: AnimationPriority.ATTACK,\n * };\n * \n * // Timestamp strategy: current wins (1000 < 1001)\n * resolveConflict(current, requested, \"timestamp\"); // \"current\"\n * \n * // Requested strategy: always take new animation\n * resolveConflict(current, requested, \"requested\"); // \"requested\"\n * ```\n * \n * @korean 충돌해결\n */\nexport function resolveConflict(\n current: AnimationRequest,\n requested: AnimationRequest,\n strategy: ConflictResolutionStrategy = \"timestamp\"\n): \"current\" | \"requested\" {\n // If only one is forced, forced wins\n if (requested.forced && !current.forced) {\n return \"requested\";\n }\n if (current.forced && !requested.forced) {\n return \"current\";\n }\n \n // If both are forced OR both are normal, use conflict resolution strategy\n\n // Apply conflict resolution strategy\n switch (strategy) {\n case \"timestamp\":\n // Earlier timestamp wins (FIFO)\n return current.timestamp <= requested.timestamp ? \"current\" : \"requested\";\n \n case \"state_order\":\n // Use enum value comparison as tiebreaker\n return current.state <= requested.state ? \"current\" : \"requested\";\n \n case \"current\":\n // Always keep current animation\n return \"current\";\n \n case \"requested\":\n // Always accept requested animation\n return \"requested\";\n \n default:\n // Default to timestamp strategy\n return current.timestamp <= requested.timestamp ? \"current\" : \"requested\";\n }\n}\n\n// ===== Animation Queue System (애니메이션 대기열) =====\n\n/**\n * Animation queue for managing pending animations\n * \n * **Korean**: 애니메이션 대기열\n * \n * Stores pending animation requests that should play after the current\n * animation completes. Useful for:\n * - Buffering input during non-interruptible animations\n * - Creating animation chains/combos\n * - Handling rapid input sequences\n * \n * Queue is priority-ordered: highest priority animations are dequeued first.\n * \n * @korean 애니메이션대기열\n */\nexport class AnimationQueue {\n private queue: AnimationRequest[] = [];\n private maxSize: number;\n private conflictStrategy: ConflictResolutionStrategy;\n\n /**\n * Create a new animation queue\n * \n * @param maxSize - Maximum queue size (default: 3)\n * @param conflictStrategy - Strategy for resolving equal-priority conflicts\n * \n * @korean 생성자\n */\n constructor(\n maxSize: number = 3,\n conflictStrategy: ConflictResolutionStrategy = \"timestamp\"\n ) {\n this.maxSize = maxSize;\n this.conflictStrategy = conflictStrategy;\n }\n\n /**\n * Add animation request to queue\n * \n * **Korean**: 대기열에 추가\n * \n * Adds an animation request to the queue if space is available.\n * Queue is kept sorted by priority (highest first).\n * \n * @param request - Animation request to enqueue\n * @returns Whether request was successfully enqueued\n * \n * @korean 대기열추가\n */\n enqueue(request: AnimationRequest): boolean {\n // Check if queue is full\n if (this.queue.length >= this.maxSize) {\n // Queue is sorted by priority (highest first), so last element is lowest priority\n const lowestPriorityItem = this.queue[this.queue.length - 1];\n \n if (request.priority > lowestPriorityItem.priority) {\n // Remove lowest priority item to make space\n this.queue.pop();\n } else {\n return false; // Queue full, request discarded\n }\n }\n\n // Insert request into queue while maintaining sort by priority (highest first).\n // Use binary search (O(log n)) to find insertion position, then splice (O(n)) to insert.\n // Overall: O(n) per insertion, avoiding an O(n log n) full sort of the entire queue.\n let low = 0;\n let high = this.queue.length;\n\n // Binary search to find insertion index\n while (low < high) {\n const mid = (low + high) >> 1;\n const midPriority = this.queue[mid].priority;\n\n if (midPriority < request.priority) {\n // New request has higher priority; search left half\n high = mid;\n } else {\n // New request has equal or lower priority; search right half\n low = mid + 1;\n }\n }\n\n // Insert at computed index to keep queue sorted (highest priority first)\n this.queue.splice(low, 0, request);\n \n return true;\n }\n\n /**\n * Remove and return highest priority request from queue\n * \n * **Korean**: 대기열에서 제거\n * \n * Dequeues the highest priority animation request.\n * If multiple requests have equal priority, uses conflict resolution strategy.\n * \n * @returns Next animation request or null if queue is empty\n * \n * @korean 대기열제거\n */\n dequeue(): AnimationRequest | null {\n if (this.queue.length === 0) {\n return null;\n }\n\n // Get all requests with highest priority\n const highestPriority = this.queue[0].priority;\n const highestPriorityRequests = this.queue.filter(\n r => r.priority === highestPriority\n );\n\n let selectedRequest: AnimationRequest;\n\n if (highestPriorityRequests.length === 1) {\n selectedRequest = highestPriorityRequests[0];\n } else {\n // Multiple equal-priority requests - use conflict resolution\n selectedRequest = highestPriorityRequests.reduce((current, next) => {\n const winner = resolveConflict(current, next, this.conflictStrategy);\n return winner === \"current\" ? current : next;\n });\n }\n\n // Remove selected request from queue\n const index = this.queue.indexOf(selectedRequest);\n this.queue.splice(index, 1);\n\n return selectedRequest;\n }\n\n /**\n * Peek at next request without removing it\n * \n * **Korean**: 다음 요청 확인\n * \n * @returns Next animation request or null if queue is empty\n * \n * @korean 다음요청확인\n */\n peek(): AnimationRequest | null {\n return this.queue.length > 0 ? this.queue[0] : null;\n }\n\n /**\n * Clear all pending requests\n * \n * **Korean**: 대기열 초기화\n * \n * @korean 대기열초기화\n */\n clear(): void {\n this.queue = [];\n }\n\n /**\n * Get number of pending requests\n * \n * **Korean**: 대기열 크기\n * \n * @returns Number of pending requests\n * \n * @korean 대기열크기\n */\n size(): number {\n return this.queue.length;\n }\n\n /**\n * Check if queue is empty\n * \n * **Korean**: 대기열 비어있음\n * \n * @returns True if queue has no pending requests\n * \n * @korean 대기열비어있음\n */\n isEmpty(): boolean {\n return this.queue.length === 0;\n }\n\n /**\n * Check if queue is full\n * \n * **Korean**: 대기열 가득참\n * \n * @returns True if queue is at maximum capacity\n * \n * @korean 대기열가득찬\n */\n isFull(): boolean {\n return this.queue.length >= this.maxSize;\n }\n\n /**\n * Get all pending requests (read-only)\n * \n * **Korean**: 모든 대기 요청\n * \n * @returns Array of pending requests\n * \n * @korean 모든대기요청\n */\n getAll(): readonly AnimationRequest[] {\n return [...this.queue];\n }\n\n /**\n * Get maximum queue size\n * \n * **Korean**: 최대 대기열 크기\n * \n * @returns Maximum queue capacity\n * \n * @korean 최대대기열크기\n */\n getMaxSize(): number {\n return this.maxSize;\n }\n\n /**\n * Set conflict resolution strategy\n * \n * **Korean**: 충돌 해결 전략 설정\n * \n * Updates the strategy used to resolve equal-priority conflicts.\n * \n * @param strategy - New conflict resolution strategy\n * \n * @korean 충돌해결전략설정\n */\n setConflictStrategy(strategy: ConflictResolutionStrategy): void {\n this.conflictStrategy = strategy;\n }\n}\n\n// ===== Interruptibility Windows (중단 가능 구간) =====\n\n/**\n * Interruptibility window definition\n * \n * **Korean**: 중단 가능 구간\n * \n * Defines specific frame ranges where an animation can be interrupted,\n * allowing for more nuanced control than simple boolean interruptibility.\n * \n * Example: Attack animation might be interruptible only in startup frames (0-3)\n * and recovery frames (10-12), but not during active frames (4-9).\n * \n * @korean 중단가능구간\n */\nexport interface InterruptibilityWindow {\n /** Starting frame (inclusive) */\n readonly startFrame: number;\n /** Ending frame (inclusive) */\n readonly endFrame: number;\n /** Minimum priority required to interrupt during this window */\n readonly minPriorityToInterrupt: AnimationPriority;\n}\n\n/**\n * Check if an animation can be interrupted at a specific frame\n * \n * **Korean**: 프레임별 중단 가능 여부\n * \n * Determines if an animation can be interrupted based on:\n * - Current frame index\n * - Interruptibility windows\n * - Requested animation priority\n * \n * @param currentFrame - Current frame index in animation\n * @param requestedPriority - Priority of animation trying to interrupt\n * @param windows - Array of interruptibility windows\n * @returns Whether animation can be interrupted at this frame\n * \n * @example\n * ```typescript\n * const attackWindows: InterruptibilityWindow[] = [\n * { startFrame: 0, endFrame: 3, minPriorityToInterrupt: AnimationPriority.ATTACK },\n * { startFrame: 10, endFrame: 12, minPriorityToInterrupt: AnimationPriority.DEFEND },\n * ];\n * \n * // Frame 2: interruptible by attacks or higher\n * canInterruptAtFrame(2, AnimationPriority.HIT, attackWindows); // true\n * canInterruptAtFrame(2, AnimationPriority.DEFEND, attackWindows); // false\n * \n * // Frame 5: not interruptible (no window)\n * canInterruptAtFrame(5, AnimationPriority.KO, attackWindows); // false\n * ```\n * \n * @korean 프레임별중단가능여부\n */\nexport function canInterruptAtFrame(\n currentFrame: number,\n requestedPriority: AnimationPriority,\n windows: readonly InterruptibilityWindow[]\n): boolean {\n // Find window that contains current frame\n const activeWindow = windows.find(\n w => currentFrame >= w.startFrame && currentFrame <= w.endFrame\n );\n\n // If no window contains this frame, cannot interrupt\n if (!activeWindow) {\n return false;\n }\n\n // Check if requested priority meets window's minimum\n return requestedPriority >= activeWindow.minPriorityToInterrupt;\n}\n\n/**\n * Get interruptibility window for current frame\n * \n * **Korean**: 현재 프레임 중단 가능 구간\n * \n * Returns the active interruptibility window for the given frame,\n * or null if frame is not in any window.\n * \n * @param currentFrame - Current frame index\n * @param windows - Array of interruptibility windows\n * @returns Active window or null\n * \n * @korean 현재프레임중단가능구간\n */\nexport function getInterruptibilityWindow(\n currentFrame: number,\n windows: readonly InterruptibilityWindow[]\n): InterruptibilityWindow | null {\n return windows.find(\n w => currentFrame >= w.startFrame && currentFrame <= w.endFrame\n ) ?? null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AnimationPriority.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationPriority.ts"],"sourcesContent":["/**\n * Animation priority system for Black Trigram\n * \n * Determines which animations can interrupt others based on priority levels.\n * Higher priority animations can interrupt lower priority ones.\n * \n * Priority order: recovery > fall > ko > hit > attack > defend > stance_change > movement > idle\n * \n * Enhanced with:\n * - Deterministic conflict resolution for simultaneous equal-priority animations\n * - Animation queue system for pending animations\n * - Interruptibility windows (frame-based interrupt control)\n * - Korean terminology for all priority levels and conflicts\n * \n * @module systems/animation/AnimationPriority\n * @category Animation\n * @korean 애니메이션우선순위\n */\n\nimport { AnimationPriority, AnimationState, STEP_PRIORITY } from \"./types\";\n\n/**\n * Map animation states to their priority levels\n * \n * Stance guard animations have same priority as idle (0) since they are\n * also idle states, just stance-specific.\n * Tactical steps have priority 5 (same as attacks) to ensure commitment.\n * \n * @korean 애니메이션우선순위맵\n */\nexport const ANIMATION_PRIORITY_MAP: Record<AnimationState, AnimationPriority> = {\n idle: AnimationPriority.IDLE,\n walk: AnimationPriority.WALK,\n run: AnimationPriority.RUN,\n stance_change: AnimationPriority.STANCE_CHANGE,\n stance_side_switch: AnimationPriority.STANCE_CHANGE, // Same priority as stance_change\n defend: AnimationPriority.DEFEND,\n // Defensive animations (방어 애니메이션)\n defend_block_success: AnimationPriority.HIT, // Priority 6 - same as hit\n defend_parry: AnimationPriority.KO, // Priority 7 - higher than block\n defend_guard_break: AnimationPriority.FALL, // Priority 8 - highest (same as fall)\n defend_recovery: AnimationPriority.RUN, // Priority 2 - interruptible recovery\n attack: AnimationPriority.ATTACK,\n hit: AnimationPriority.HIT,\n ko: AnimationPriority.KO,\n // Stance-specific guard animations (팔괘 방어 자세)\n stance_guard_geon: AnimationPriority.IDLE,\n stance_guard_tae: AnimationPriority.IDLE,\n stance_guard_li: AnimationPriority.IDLE,\n stance_guard_jin: AnimationPriority.IDLE,\n stance_guard_son: AnimationPriority.IDLE,\n stance_guard_gam: AnimationPriority.IDLE,\n stance_guard_gan: AnimationPriority.IDLE,\n stance_guard_gon: AnimationPriority.IDLE,\n // Tactical step animations (전술적 발걸음) - non-interruptible\n step_forward: STEP_PRIORITY,\n step_back: STEP_PRIORITY,\n step_left: STEP_PRIORITY,\n step_right: STEP_PRIORITY,\n step_forward_left: STEP_PRIORITY,\n step_forward_right: STEP_PRIORITY,\n step_back_left: STEP_PRIORITY,\n step_back_right: STEP_PRIORITY,\n // Fall animations (낙법) - highest priority\n fall_forward: AnimationPriority.FALL,\n fall_backward: AnimationPriority.FALL,\n fall_side_left: AnimationPriority.FALL,\n fall_side_right: AnimationPriority.FALL,\n // Ground states (지면 자세) - idle priority\n ground_prone: AnimationPriority.IDLE,\n ground_supine: AnimationPriority.IDLE,\n ground_side_left: AnimationPriority.IDLE,\n ground_side_right: AnimationPriority.IDLE,\n // 180-degree turn animations (180도 회전) - same as steps (committed action)\n turn_left: STEP_PRIORITY,\n turn_right: STEP_PRIORITY,\n // Footwork patterns (보법) - Korean martial arts specialized footwork\n footwork_circular_left: STEP_PRIORITY,\n footwork_circular_right: STEP_PRIORITY,\n footwork_pivot_left: STEP_PRIORITY,\n footwork_pivot_right: STEP_PRIORITY,\n footwork_slide_forward: AnimationPriority.DEFEND,\n footwork_slide_back: AnimationPriority.DEFEND,\n footwork_slide_left: AnimationPriority.DEFEND,\n footwork_slide_right: AnimationPriority.DEFEND,\n footwork_shuffle: AnimationPriority.STANCE_CHANGE,\n // Recovery animations (기상 애니메이션) - highest priority (can interrupt anything)\n recovery_prone_standup: AnimationPriority.RECOVERY,\n recovery_supine_standup: AnimationPriority.RECOVERY,\n recovery_roll: AnimationPriority.RECOVERY,\n recovery_defensive: AnimationPriority.RECOVERY,\n // Grappling animations (잡기 애니메이션) - high priority like attacks/hits\n grapple_entry: AnimationPriority.ATTACK, // Priority 5 - same as attack\n grapple_control: AnimationPriority.ATTACK, // Priority 5 - maintain control\n grapple_struggle: AnimationPriority.HIT, // Priority 6 - struggling escape\n grapple_escape: AnimationPriority.HIT, // Priority 6 - successful escape\n};\n\n/**\n * Check if an animation can interrupt another based on priority\n * \n * @param current - Current animation state\n * @param requested - Requested animation state\n * @param currentInterruptible - Whether current animation is interruptible\n * @returns Whether the requested animation can interrupt the current one\n * \n * @example\n * ```typescript\n * // Hit can interrupt attack\n * canInterrupt(\"attack\", \"hit\", true); // true\n * \n * // Attack cannot interrupt hit\n * canInterrupt(\"hit\", \"attack\", true); // false\n * \n * // Nothing can interrupt non-interruptible animations\n * canInterrupt(\"attack\", \"hit\", false); // false (unless same priority)\n * ```\n * \n * @korean 중단가능여부확인\n */\nexport function canInterrupt(\n current: AnimationState,\n requested: AnimationState,\n currentInterruptible: boolean\n): boolean {\n const currentPriority = ANIMATION_PRIORITY_MAP[current];\n const requestedPriority = ANIMATION_PRIORITY_MAP[requested];\n\n // Same priority animations can always transition\n if (currentPriority === requestedPriority) {\n return true;\n }\n\n // Non-interruptible animations can only be interrupted by higher priority\n if (!currentInterruptible) {\n return requestedPriority > currentPriority;\n }\n\n // Interruptible animations can be interrupted by same or higher priority\n return requestedPriority >= currentPriority;\n}\n\n/**\n * Get the priority level for an animation state\n * \n * @param state - Animation state\n * @returns Priority level\n * \n * @korean 우선순위가져오기\n */\nexport function getPriority(state: AnimationState): AnimationPriority {\n return ANIMATION_PRIORITY_MAP[state];\n}\n\n/**\n * Compare two animation priorities\n * \n * @param state1 - First animation state\n * @param state2 - Second animation state\n * @returns Positive if state1 has higher priority, negative if state2 has higher priority, 0 if equal\n * \n * @korean 우선순위비교\n */\nexport function comparePriority(\n state1: AnimationState,\n state2: AnimationState\n): number {\n return ANIMATION_PRIORITY_MAP[state1] - ANIMATION_PRIORITY_MAP[state2];\n}\n\n// ===== Korean Priority Level Terminology (우선순위 등급 용어) =====\n\n/**\n * Korean names for each priority level\n * \n * Maps priority levels to their Korean martial arts terminology:\n * - IDLE (0): 대기 (Daegi) - Waiting/Ready state\n * - WALK (1): 보행 (Bohaeng) - Walking movement\n * - RUN (2): 질주 (Jilju) - Running movement\n * - STANCE_CHANGE (3): 자세전환 (Jase Jeonhwan) - Stance transition\n * - DEFEND (4): 방어 (Bangeo) - Defense action\n * - ATTACK (5): 공격 (Gonggyeok) - Attack action\n * - HIT (6): 피격 (Pigyeok) - Being hit\n * - KO (7): 기절 (Gijeol) - Knockout\n * - FALL (8): 낙법 (Nakbeop) - Falling technique\n * - RECOVERY (9): 기상 (Gisang) - Recovery/Getting up\n * \n * @korean 우선순위한글용어\n */\nexport const PRIORITY_LEVEL_KOREAN_NAMES: Record<AnimationPriority, {\n korean: string;\n romanized: string;\n english: string;\n}> = {\n [AnimationPriority.IDLE]: {\n korean: \"대기\",\n romanized: \"Daegi\",\n english: \"Ready State\",\n },\n [AnimationPriority.WALK]: {\n korean: \"보행\",\n romanized: \"Bohaeng\",\n english: \"Walking\",\n },\n [AnimationPriority.RUN]: {\n korean: \"질주\",\n romanized: \"Jilju\",\n english: \"Running\",\n },\n [AnimationPriority.STANCE_CHANGE]: {\n korean: \"자세전환\",\n romanized: \"Jase Jeonhwan\",\n english: \"Stance Change\",\n },\n [AnimationPriority.DEFEND]: {\n korean: \"방어\",\n romanized: \"Bangeo\",\n english: \"Defense\",\n },\n [AnimationPriority.ATTACK]: {\n korean: \"공격\",\n romanized: \"Gonggyeok\",\n english: \"Attack\",\n },\n [AnimationPriority.HIT]: {\n korean: \"피격\",\n romanized: \"Pigyeok\",\n english: \"Hit\",\n },\n [AnimationPriority.KO]: {\n korean: \"기절\",\n romanized: \"Gijeol\",\n english: \"Knockout\",\n },\n [AnimationPriority.FALL]: {\n korean: \"낙법\",\n romanized: \"Nakbeop\",\n english: \"Falling\",\n },\n [AnimationPriority.RECOVERY]: {\n korean: \"기상\",\n romanized: \"Gisang\",\n english: \"Recovery\",\n },\n};\n\n/**\n * Get Korean name for a priority level\n * \n * @param priority - Animation priority level\n * @returns Korean terminology object\n * \n * @korean 우선순위한글이름가져오기\n */\nexport function getPriorityKoreanName(priority: AnimationPriority): {\n korean: string;\n romanized: string;\n english: string;\n} {\n return PRIORITY_LEVEL_KOREAN_NAMES[priority];\n}\n\n// ===== Conflict Resolution (충돌 해결) =====\n\n/**\n * Conflict resolution strategy for equal-priority animations\n * \n * **Korean**: 충돌 해결 전략\n * \n * Determines how to handle when two animations of equal priority\n * are requested simultaneously:\n * \n * - `timestamp`: First animation requested wins (FIFO - 선입선출)\n * - `state_order`: Use AnimationState enum order as tiebreaker\n * - `current`: Keep current animation running\n * - `requested`: Always switch to requested animation\n * \n * @korean 충돌해결전략\n */\nexport type ConflictResolutionStrategy =\n | \"timestamp\" // 시간순서 (First requested wins)\n | \"state_order\" // 상태순서 (Enum order tiebreaker)\n | \"current\" // 현재유지 (Keep current)\n | \"requested\"; // 요청수락 (Accept requested)\n\n/**\n * Animation request with timestamp for conflict resolution\n * \n * **Korean**: 애니메이션 요청\n * \n * Tracks animation requests with timestamps to enable deterministic\n * conflict resolution when multiple equal-priority animations compete.\n * \n * @korean 애니메이션요청\n */\nexport interface AnimationRequest {\n /** Requested animation state */\n readonly state: AnimationState;\n /** Request timestamp in milliseconds (from performance.now()) */\n readonly timestamp: number;\n /** Request priority */\n readonly priority: AnimationPriority;\n /** \n * Whether this is a forced request (wins conflict resolution against non-forced requests)\n * \n * Note: This only affects conflict resolution between equal-priority animations in the queue.\n * It does not bypass priority checks or interrupt rules for the initial transition.\n * \n * @korean 강제요청\n */\n readonly forced?: boolean;\n}\n\n/**\n * Resolve conflict between two equal-priority animations\n * \n * **Korean**: 충돌 해결\n * \n * Uses the specified strategy to deterministically decide which animation\n * should play when both have equal priority.\n * \n * @param current - Current animation request\n * @param requested - Requested animation request\n * @param strategy - Conflict resolution strategy\n * @returns Which animation should play: 'current' or 'requested'\n * \n * @example\n * ```typescript\n * const current = {\n * state: AnimationState.ATTACK,\n * timestamp: 1000,\n * priority: AnimationPriority.ATTACK,\n * };\n * const requested = {\n * state: AnimationState.STEP_FORWARD,\n * timestamp: 1001,\n * priority: AnimationPriority.ATTACK,\n * };\n * \n * // Timestamp strategy: current wins (1000 < 1001)\n * resolveConflict(current, requested, \"timestamp\"); // \"current\"\n * \n * // Requested strategy: always take new animation\n * resolveConflict(current, requested, \"requested\"); // \"requested\"\n * ```\n * \n * @korean 충돌해결\n */\nexport function resolveConflict(\n current: AnimationRequest,\n requested: AnimationRequest,\n strategy: ConflictResolutionStrategy = \"timestamp\"\n): \"current\" | \"requested\" {\n // If only one is forced, forced wins\n if (requested.forced && !current.forced) {\n return \"requested\";\n }\n if (current.forced && !requested.forced) {\n return \"current\";\n }\n \n // If both are forced OR both are normal, use conflict resolution strategy\n\n // Apply conflict resolution strategy\n switch (strategy) {\n case \"timestamp\":\n // Earlier timestamp wins (FIFO)\n return current.timestamp <= requested.timestamp ? \"current\" : \"requested\";\n \n case \"state_order\":\n // Use enum value comparison as tiebreaker\n return current.state <= requested.state ? \"current\" : \"requested\";\n \n case \"current\":\n // Always keep current animation\n return \"current\";\n \n case \"requested\":\n // Always accept requested animation\n return \"requested\";\n \n default:\n // Default to timestamp strategy\n return current.timestamp <= requested.timestamp ? \"current\" : \"requested\";\n }\n}\n\n// ===== Animation Queue System (애니메이션 대기열) =====\n\n/**\n * Animation queue for managing pending animations\n * \n * **Korean**: 애니메이션 대기열\n * \n * Stores pending animation requests that should play after the current\n * animation completes. Useful for:\n * - Buffering input during non-interruptible animations\n * - Creating animation chains/combos\n * - Handling rapid input sequences\n * \n * Queue is priority-ordered: highest priority animations are dequeued first.\n * \n * @korean 애니메이션대기열\n */\nexport class AnimationQueue {\n private queue: AnimationRequest[] = [];\n private maxSize: number;\n private conflictStrategy: ConflictResolutionStrategy;\n\n /**\n * Create a new animation queue\n * \n * @param maxSize - Maximum queue size (default: 3)\n * @param conflictStrategy - Strategy for resolving equal-priority conflicts\n * \n * @korean 생성자\n */\n constructor(\n maxSize: number = 3,\n conflictStrategy: ConflictResolutionStrategy = \"timestamp\"\n ) {\n this.maxSize = maxSize;\n this.conflictStrategy = conflictStrategy;\n }\n\n /**\n * Add animation request to queue\n * \n * **Korean**: 대기열에 추가\n * \n * Adds an animation request to the queue if space is available.\n * Queue is kept sorted by priority (highest first).\n * \n * @param request - Animation request to enqueue\n * @returns Whether request was successfully enqueued\n * \n * @korean 대기열추가\n */\n enqueue(request: AnimationRequest): boolean {\n // Check if queue is full\n if (this.queue.length >= this.maxSize) {\n // Queue is sorted by priority (highest first), so last element is lowest priority\n const lowestPriorityItem = this.queue[this.queue.length - 1];\n \n if (request.priority > lowestPriorityItem.priority) {\n // Remove lowest priority item to make space\n this.queue.pop();\n } else {\n return false; // Queue full, request discarded\n }\n }\n\n // Insert request into queue while maintaining sort by priority (highest first).\n // Use binary search (O(log n)) to find insertion position, then splice (O(n)) to insert.\n // Overall: O(n) per insertion, avoiding an O(n log n) full sort of the entire queue.\n let low = 0;\n let high = this.queue.length;\n\n // Binary search to find insertion index\n while (low < high) {\n const mid = (low + high) >> 1;\n const midPriority = this.queue[mid].priority;\n\n if (midPriority < request.priority) {\n // New request has higher priority; search left half\n high = mid;\n } else {\n // New request has equal or lower priority; search right half\n low = mid + 1;\n }\n }\n\n // Insert at computed index to keep queue sorted (highest priority first)\n this.queue.splice(low, 0, request);\n \n return true;\n }\n\n /**\n * Remove and return highest priority request from queue\n * \n * **Korean**: 대기열에서 제거\n * \n * Dequeues the highest priority animation request.\n * If multiple requests have equal priority, uses conflict resolution strategy.\n * \n * @returns Next animation request or null if queue is empty\n * \n * @korean 대기열제거\n */\n dequeue(): AnimationRequest | null {\n if (this.queue.length === 0) {\n return null;\n }\n\n // Get all requests with highest priority\n const highestPriority = this.queue[0].priority;\n const highestPriorityRequests = this.queue.filter(\n r => r.priority === highestPriority\n );\n\n let selectedRequest: AnimationRequest;\n\n if (highestPriorityRequests.length === 1) {\n selectedRequest = highestPriorityRequests[0];\n } else {\n // Multiple equal-priority requests - use conflict resolution\n selectedRequest = highestPriorityRequests.reduce((current, next) => {\n const winner = resolveConflict(current, next, this.conflictStrategy);\n return winner === \"current\" ? current : next;\n });\n }\n\n // Remove selected request from queue\n const index = this.queue.indexOf(selectedRequest);\n this.queue.splice(index, 1);\n\n return selectedRequest;\n }\n\n /**\n * Peek at next request without removing it\n * \n * **Korean**: 다음 요청 확인\n * \n * @returns Next animation request or null if queue is empty\n * \n * @korean 다음요청확인\n */\n peek(): AnimationRequest | null {\n return this.queue.length > 0 ? this.queue[0] : null;\n }\n\n /**\n * Clear all pending requests\n * \n * **Korean**: 대기열 초기화\n * \n * @korean 대기열초기화\n */\n clear(): void {\n this.queue = [];\n }\n\n /**\n * Get number of pending requests\n * \n * **Korean**: 대기열 크기\n * \n * @returns Number of pending requests\n * \n * @korean 대기열크기\n */\n size(): number {\n return this.queue.length;\n }\n\n /**\n * Check if queue is empty\n * \n * **Korean**: 대기열 비어있음\n * \n * @returns True if queue has no pending requests\n * \n * @korean 대기열비어있음\n */\n isEmpty(): boolean {\n return this.queue.length === 0;\n }\n\n /**\n * Check if queue is full\n * \n * **Korean**: 대기열 가득참\n * \n * @returns True if queue is at maximum capacity\n * \n * @korean 대기열가득찬\n */\n isFull(): boolean {\n return this.queue.length >= this.maxSize;\n }\n\n /**\n * Get all pending requests (read-only)\n * \n * **Korean**: 모든 대기 요청\n * \n * @returns Array of pending requests\n * \n * @korean 모든대기요청\n */\n getAll(): readonly AnimationRequest[] {\n return [...this.queue];\n }\n\n /**\n * Get maximum queue size\n * \n * **Korean**: 최대 대기열 크기\n * \n * @returns Maximum queue capacity\n * \n * @korean 최대대기열크기\n */\n getMaxSize(): number {\n return this.maxSize;\n }\n\n /**\n * Set conflict resolution strategy\n * \n * **Korean**: 충돌 해결 전략 설정\n * \n * Updates the strategy used to resolve equal-priority conflicts.\n * \n * @param strategy - New conflict resolution strategy\n * \n * @korean 충돌해결전략설정\n */\n setConflictStrategy(strategy: ConflictResolutionStrategy): void {\n this.conflictStrategy = strategy;\n }\n}\n\n// ===== Interruptibility Windows (중단 가능 구간) =====\n\n/**\n * Interruptibility window definition\n * \n * **Korean**: 중단 가능 구간\n * \n * Defines specific frame ranges where an animation can be interrupted,\n * allowing for more nuanced control than simple boolean interruptibility.\n * \n * Example: Attack animation might be interruptible only in startup frames (0-3)\n * and recovery frames (10-12), but not during active frames (4-9).\n * \n * @korean 중단가능구간\n */\nexport interface InterruptibilityWindow {\n /** Starting frame (inclusive) */\n readonly startFrame: number;\n /** Ending frame (inclusive) */\n readonly endFrame: number;\n /** Minimum priority required to interrupt during this window */\n readonly minPriorityToInterrupt: AnimationPriority;\n}\n\n/**\n * Check if an animation can be interrupted at a specific frame\n * \n * **Korean**: 프레임별 중단 가능 여부\n * \n * Determines if an animation can be interrupted based on:\n * - Current frame index\n * - Interruptibility windows\n * - Requested animation priority\n * \n * @param currentFrame - Current frame index in animation\n * @param requestedPriority - Priority of animation trying to interrupt\n * @param windows - Array of interruptibility windows\n * @returns Whether animation can be interrupted at this frame\n * \n * @example\n * ```typescript\n * const attackWindows: InterruptibilityWindow[] = [\n * { startFrame: 0, endFrame: 3, minPriorityToInterrupt: AnimationPriority.ATTACK },\n * { startFrame: 10, endFrame: 12, minPriorityToInterrupt: AnimationPriority.DEFEND },\n * ];\n * \n * // Frame 2: interruptible by attacks or higher\n * canInterruptAtFrame(2, AnimationPriority.HIT, attackWindows); // true\n * canInterruptAtFrame(2, AnimationPriority.DEFEND, attackWindows); // false\n * \n * // Frame 5: not interruptible (no window)\n * canInterruptAtFrame(5, AnimationPriority.KO, attackWindows); // false\n * ```\n * \n * @korean 프레임별중단가능여부\n */\nexport function canInterruptAtFrame(\n currentFrame: number,\n requestedPriority: AnimationPriority,\n windows: readonly InterruptibilityWindow[]\n): boolean {\n // Find window that contains current frame\n const activeWindow = windows.find(\n w => currentFrame >= w.startFrame && currentFrame <= w.endFrame\n );\n\n // If no window contains this frame, cannot interrupt\n if (!activeWindow) {\n return false;\n }\n\n // Check if requested priority meets window's minimum\n return requestedPriority >= activeWindow.minPriorityToInterrupt;\n}\n\n/**\n * Get interruptibility window for current frame\n * \n * **Korean**: 현재 프레임 중단 가능 구간\n * \n * Returns the active interruptibility window for the given frame,\n * or null if frame is not in any window.\n * \n * @param currentFrame - Current frame index\n * @param windows - Array of interruptibility windows\n * @returns Active window or null\n * \n * @korean 현재프레임중단가능구간\n */\nexport function getInterruptibilityWindow(\n currentFrame: number,\n windows: readonly InterruptibilityWindow[]\n): InterruptibilityWindow | null {\n return windows.find(\n w => currentFrame >= w.startFrame && currentFrame <= w.endFrame\n ) ?? null;\n}\n"],"mappings":"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"}