blacktrigram 0.7.39 → 0.7.41

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 (354) hide show
  1. package/lib/App2.js.map +1 -1
  2. package/lib/audio/AudioAssetLoader.js.map +1 -1
  3. package/lib/audio/AudioAssetRegistry.js.map +1 -1
  4. package/lib/audio/AudioCache.js.map +1 -1
  5. package/lib/audio/AudioManager.js.map +1 -1
  6. package/lib/audio/AudioMonitor.js.map +1 -1
  7. package/lib/audio/AudioPool.js.map +1 -1
  8. package/lib/audio/AudioProvider.js.map +1 -1
  9. package/lib/audio/AudioUtils.js.map +1 -1
  10. package/lib/audio/BoneImpactAudioMap.js.map +1 -1
  11. package/lib/audio/VariantSelector.js.map +1 -1
  12. package/lib/audio/types.js.map +1 -1
  13. package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
  14. package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
  15. package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
  16. package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
  17. package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
  18. package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
  19. package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
  20. package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
  21. package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
  22. package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
  23. package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
  24. package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
  25. package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
  26. package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
  27. package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
  28. package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
  29. package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
  30. package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
  31. package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
  32. package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
  33. package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
  34. package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
  35. package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
  36. package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
  37. package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
  38. package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
  39. package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
  40. package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
  41. package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
  42. package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
  43. package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
  44. package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
  45. package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
  46. package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
  47. package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
  48. package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
  49. package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
  50. package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
  51. package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
  52. package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
  53. package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
  54. package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
  55. package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
  56. package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
  57. package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
  58. package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
  59. package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
  60. package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
  61. package/lib/components/screens/controls/components/Key3D.js.map +1 -1
  62. package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
  63. package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
  64. package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
  65. package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
  66. package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
  67. package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
  68. package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
  69. package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
  70. package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
  71. package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
  72. package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
  73. package/lib/components/screens/intro/IntroScreen3D.js +1 -1
  74. package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
  75. package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
  76. package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
  77. package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
  78. package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
  79. package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
  80. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
  81. package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
  82. package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
  83. package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
  84. package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
  85. package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
  86. package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
  87. package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
  88. package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
  89. package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
  90. package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
  91. package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
  92. package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
  93. package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
  94. package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
  95. package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
  96. package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
  97. package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
  98. package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
  99. package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
  100. package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
  101. package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
  102. package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
  103. package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
  104. package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
  105. package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
  106. package/lib/components/shared/base/BaseButton.js.map +1 -1
  107. package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
  108. package/lib/components/shared/base/BasePanel.js.map +1 -1
  109. package/lib/components/shared/base/BaseText.js.map +1 -1
  110. package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
  111. package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
  112. package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
  113. package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
  114. package/lib/components/shared/mobile/HapticController.js.map +1 -1
  115. package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
  116. package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
  117. package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
  118. package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
  119. package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
  120. package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
  121. package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
  122. package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
  123. package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
  124. package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
  125. package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
  126. package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
  127. package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
  128. package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
  129. package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
  130. package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
  131. package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
  132. package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
  133. package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
  134. package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
  135. package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
  136. package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
  137. package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
  138. package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
  139. package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
  140. package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
  141. package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
  142. package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
  143. package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
  144. package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
  145. package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
  146. package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
  147. package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
  148. package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
  149. package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
  150. package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
  151. package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
  152. package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
  153. package/lib/components/shared/three/ui/MenuList.js.map +1 -1
  154. package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
  155. package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
  156. package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
  157. package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
  158. package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
  159. package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
  160. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
  161. package/lib/components/shared/ui/BackButton.js.map +1 -1
  162. package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
  163. package/lib/components/shared/ui/CombatTimer.js.map +1 -1
  164. package/lib/components/shared/ui/ErrorModal.js.map +1 -1
  165. package/lib/components/shared/ui/LoadingState.js.map +1 -1
  166. package/lib/components/shared/ui/SplashScreen.js +2 -2
  167. package/lib/components/shared/ui/SplashScreen.js.map +1 -1
  168. package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
  169. package/lib/components/shared/ui/VolumeControl.js.map +1 -1
  170. package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
  171. package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
  172. package/lib/constants/bodyDimensions.js.map +1 -1
  173. package/lib/constants/bodyRenderingConstants.js.map +1 -1
  174. package/lib/data/archetypeClothing.js.map +1 -1
  175. package/lib/data/archetypePhysicalAttributes.js.map +1 -1
  176. package/lib/data/techniqueMappings.js.map +1 -1
  177. package/lib/data/techniques.js.map +1 -1
  178. package/lib/hooks/useActionFeedback.js.map +1 -1
  179. package/lib/hooks/useBalanceAnimations.js.map +1 -1
  180. package/lib/hooks/useCombatTimer.js.map +1 -1
  181. package/lib/hooks/useDebounce.js.map +1 -1
  182. package/lib/hooks/useHUDLayout.js.map +1 -1
  183. package/lib/hooks/useHandPoseTransitions.js.map +1 -1
  184. package/lib/hooks/useKeyboardControls.js.map +1 -1
  185. package/lib/hooks/useMatchCountdown.js.map +1 -1
  186. package/lib/hooks/useMuscleActivation.js.map +1 -1
  187. package/lib/hooks/usePauseMenu.js.map +1 -1
  188. package/lib/hooks/usePlayerAnimation.js.map +1 -1
  189. package/lib/hooks/useResponsiveLayout.js.map +1 -1
  190. package/lib/hooks/useRoundTransition.js.map +1 -1
  191. package/lib/hooks/useSkeletalAnimation.js.map +1 -1
  192. package/lib/hooks/useTechniqueSelection.js.map +1 -1
  193. package/lib/hooks/useThrottle.js.map +1 -1
  194. package/lib/hooks/useTouchControls.js.map +1 -1
  195. package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
  196. package/lib/hooks/useWindowSize.js.map +1 -1
  197. package/lib/systems/CombatSystem.js.map +1 -1
  198. package/lib/systems/EffectCalculator.js.map +1 -1
  199. package/lib/systems/LayoutSystem.js.map +1 -1
  200. package/lib/systems/PlayerEffectManager.js.map +1 -1
  201. package/lib/systems/ResponsiveScaling.js.map +1 -1
  202. package/lib/systems/TrigramSystem.js.map +1 -1
  203. package/lib/systems/VitalPointSystem.js.map +1 -1
  204. package/lib/systems/ai/AIPersonality.js.map +1 -1
  205. package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
  206. package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
  207. package/lib/systems/ai/ComboSystem.js.map +1 -1
  208. package/lib/systems/ai/DecisionTree.js.map +1 -1
  209. package/lib/systems/ai/TrainingAI.js.map +1 -1
  210. package/lib/systems/ai/types.js.map +1 -1
  211. package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
  212. package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
  213. package/lib/systems/animation/builders/HandPoses.js.map +1 -1
  214. package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
  215. package/lib/systems/animation/builders/KeyframeInterpolation.js +3 -90
  216. package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
  217. package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
  218. package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
  219. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
  220. package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
  221. package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
  222. package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
  223. package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
  224. package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
  225. package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
  226. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
  227. package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
  228. package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
  229. package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
  230. package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
  231. package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
  232. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
  233. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
  234. package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
  235. package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
  236. package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
  237. package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
  238. package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
  239. package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
  240. package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
  241. package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
  242. package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
  243. package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
  244. package/lib/systems/animation/core/types.js.map +1 -1
  245. package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
  246. package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
  247. package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
  248. package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
  249. package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
  250. package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
  251. package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
  252. package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
  253. package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
  254. package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
  255. package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
  256. package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
  257. package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
  258. package/lib/systems/bodypart/types.js.map +1 -1
  259. package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
  260. package/lib/systems/breathing/feedback.js.map +1 -1
  261. package/lib/systems/breathing/integration.js.map +1 -1
  262. package/lib/systems/combat/BalanceSystem.js.map +1 -1
  263. package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
  264. package/lib/systems/combat/CombatStateSystem.js.map +1 -1
  265. package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
  266. package/lib/systems/combat/FallIntegration.js.map +1 -1
  267. package/lib/systems/combat/GrappleSystem.js.map +1 -1
  268. package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
  269. package/lib/systems/combat/PainResponseSystem.js.map +1 -1
  270. package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
  271. package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
  272. package/lib/systems/combat/typeGuards.js.map +1 -1
  273. package/lib/systems/effects.js.map +1 -1
  274. package/lib/systems/game.js.map +1 -1
  275. package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
  276. package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
  277. package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
  278. package/lib/systems/movement/integration.js.map +1 -1
  279. package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
  280. package/lib/systems/physics/CollisionDetection.js.map +1 -1
  281. package/lib/systems/physics/CoordinateMapper.js.map +1 -1
  282. package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
  283. package/lib/systems/physics/MovementPhysics.js.map +1 -1
  284. package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
  285. package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
  286. package/lib/systems/trigram/KoreanCulture.js.map +1 -1
  287. package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
  288. package/lib/systems/trigram/StanceManager.js.map +1 -1
  289. package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
  290. package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
  291. package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
  292. package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
  293. package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
  294. package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
  295. package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
  296. package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
  297. package/lib/systems/trigram/techniques/index.js.map +1 -1
  298. package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
  299. package/lib/systems/trigram/types.js.map +1 -1
  300. package/lib/systems/types.js.map +1 -1
  301. package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
  302. package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
  303. package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
  304. package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
  305. package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
  306. package/lib/types/AccessibilityTypes.js.map +1 -1
  307. package/lib/types/PhysicsTypes.js.map +1 -1
  308. package/lib/types/common.js.map +1 -1
  309. package/lib/types/constants/colors.js.map +1 -1
  310. package/lib/types/constants/designSystem.js.map +1 -1
  311. package/lib/types/constants/layout.js.map +1 -1
  312. package/lib/types/constants/performance.js.map +1 -1
  313. package/lib/types/constants/typography.js.map +1 -1
  314. package/lib/types/facial.js.map +1 -1
  315. package/lib/types/hand-animation.js.map +1 -1
  316. package/lib/types/injury.js.map +1 -1
  317. package/lib/types/physics.js.map +1 -1
  318. package/lib/types/skeletal.js.map +1 -1
  319. package/lib/types/techniqueId.js.map +1 -1
  320. package/lib/utils/accessibility.js.map +1 -1
  321. package/lib/utils/arenaWorldDimensions.js.map +1 -1
  322. package/lib/utils/assetConfig.js.map +1 -1
  323. package/lib/utils/characterScaling.js.map +1 -1
  324. package/lib/utils/colorHelpers.js.map +1 -1
  325. package/lib/utils/colorUtils.js.map +1 -1
  326. package/lib/utils/combatReadiness.js.map +1 -1
  327. package/lib/utils/controlMapping.js.map +1 -1
  328. package/lib/utils/deviceDetection.js.map +1 -1
  329. package/lib/utils/effectUtils.js.map +1 -1
  330. package/lib/utils/fabricTextures.js.map +1 -1
  331. package/lib/utils/hapticFeedback.js.map +1 -1
  332. package/lib/utils/haptics.js.map +1 -1
  333. package/lib/utils/htmlOverlayHelpers.js.map +1 -1
  334. package/lib/utils/inputSystem.js.map +1 -1
  335. package/lib/utils/koreanThemeHelpers.js.map +1 -1
  336. package/lib/utils/math.js.map +1 -1
  337. package/lib/utils/mobileLayoutHelpers.js.map +1 -1
  338. package/lib/utils/mobileUIUtils.js.map +1 -1
  339. package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
  340. package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
  341. package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
  342. package/lib/utils/performanceOptimization.js.map +1 -1
  343. package/lib/utils/player3DHelpers.js.map +1 -1
  344. package/lib/utils/playerUtils.js.map +1 -1
  345. package/lib/utils/responsiveLayout.js.map +1 -1
  346. package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
  347. package/lib/utils/responsiveOrientationConstants.js.map +1 -1
  348. package/lib/utils/safeAreaUtils.js.map +1 -1
  349. package/lib/utils/sharedPhysicsConfig.js.map +1 -1
  350. package/lib/utils/skeletonScaling.js.map +1 -1
  351. package/lib/utils/stanceHelpers.js.map +1 -1
  352. package/lib/utils/threeObjectPool.js.map +1 -1
  353. package/lib/utils/visualEffects.js.map +1 -1
  354. package/package.json +8 -8
@@ -1 +1 @@
1
- {"version":3,"file":"safeAreaUtils.js","names":[],"sources":["../../src/utils/safeAreaUtils.ts"],"sourcesContent":["/**\n * Safe area utilities for handling notched devices\n * \n * Provides CSS safe area inset helpers for iOS and Android devices\n * with notches, home indicators, and other screen intrusions.\n * \n * @module utils/safeAreaUtils\n * @category UI Utilities\n * @korean 안전영역유틸리티\n */\n\nimport { SAFE_AREA_INSETS } from \"../types/constants/ui\";\n\n/**\n * Safe area edge types\n * \n * @category Safe Area\n * @korean 안전영역가장자리타입\n */\nexport type SafeAreaEdge = \"top\" | \"bottom\" | \"left\" | \"right\";\n\n/**\n * Get CSS safe area inset with fallback\n * Returns CSS `env()` with fallback to constant values\n * \n * @param edge - Which edge to get inset for\n * @param fallback - Optional custom fallback value in pixels\n * @returns CSS string with env() and fallback\n * \n * @example\n * ```typescript\n * getSafeAreaInset('top'); // \"env(safe-area-inset-top, 44px)\"\n * getSafeAreaInset('bottom', 20); // \"env(safe-area-inset-bottom, 20px)\"\n * ```\n * \n * @public\n * @korean 안전영역삽입얻기\n */\nexport function getSafeAreaInset(\n edge: SafeAreaEdge,\n fallback?: number\n): string {\n const defaultFallback =\n edge === \"top\"\n ? SAFE_AREA_INSETS.TOP\n : edge === \"bottom\"\n ? SAFE_AREA_INSETS.BOTTOM\n : edge === \"left\"\n ? SAFE_AREA_INSETS.LEFT\n : SAFE_AREA_INSETS.RIGHT;\n\n const fallbackValue = fallback ?? defaultFallback;\n return `env(safe-area-inset-${edge}, ${fallbackValue}px)`;\n}\n\n/**\n * Get safe area padding for a container\n * Returns CSS padding object with safe area support\n * \n * @param edges - Which edges to apply safe area padding\n * @param additionalPadding - Additional padding to add (in pixels)\n * @returns CSS padding object\n * \n * @example\n * ```typescript\n * getSafeAreaPadding(['top', 'bottom'], 16);\n * // {\n * // paddingTop: \"max(16px, env(safe-area-inset-top))\",\n * // paddingBottom: \"max(16px, env(safe-area-inset-bottom))\"\n * // }\n * ```\n * \n * @public\n * @korean 안전영역패딩얻기\n */\nexport function getSafeAreaPadding(\n edges: readonly SafeAreaEdge[],\n additionalPadding = 0\n): Partial<Record<`padding${Capitalize<SafeAreaEdge>}`, string>> {\n const result: Partial<Record<`padding${Capitalize<SafeAreaEdge>}`, string>> =\n {};\n\n for (const edge of edges) {\n const capitalizedEdge =\n (edge.charAt(0).toUpperCase() + edge.slice(1)) as Capitalize<SafeAreaEdge>;\n const key = `padding${capitalizedEdge}` as const;\n\n result[key] = `max(${additionalPadding}px, ${getSafeAreaInset(edge)})`;\n }\n\n return result;\n}\n\n/**\n * Get safe area aware positioning styles\n * Useful for fixed/absolute positioned elements\n * \n * @param edge - Which edge to position from\n * @param baseOffset - Base offset in pixels\n * @returns CSS positioning string\n * \n * @example\n * ```typescript\n * getSafeAreaPosition('bottom', 20);\n * // \"calc(20px + env(safe-area-inset-bottom, 34px))\"\n * ```\n * \n * @public\n * @korean 안전영역위치얻기\n */\nexport function getSafeAreaPosition(\n edge: SafeAreaEdge,\n baseOffset = 0\n): string {\n return `calc(${baseOffset}px + ${getSafeAreaInset(edge)})`;\n}\n\n/**\n * Get safe area aware height calculation\n * Useful for full-height containers\n * \n * @param excludeEdges - Which edges to exclude from height\n * @returns CSS calc() string for height\n * \n * @example\n * ```typescript\n * getSafeAreaHeight(['top', 'bottom']);\n * // \"calc(100vh - env(safe-area-inset-top, 44px) - env(safe-area-inset-bottom, 34px))\"\n * ```\n * \n * @public\n * @korean 안전영역높이얻기\n */\nexport function getSafeAreaHeight(\n excludeEdges: readonly SafeAreaEdge[]\n): string {\n const insets = excludeEdges.map((edge) => getSafeAreaInset(edge)).join(\" - \");\n return `calc(100vh - ${insets})`;\n}\n\n/**\n * Check if device likely has safe area insets\n * Uses user agent detection (not perfect but useful)\n * \n * @returns Whether device likely has safe area insets\n * \n * @example\n * ```typescript\n * if (hasSafeAreaInsets()) {\n * // Apply safe area styles\n * }\n * ```\n * \n * @public\n * @korean 안전영역삽입여부\n */\nexport function hasSafeAreaInsets(): boolean {\n // Check if CSS env() is supported\n if (typeof CSS !== \"undefined\" && CSS.supports) {\n return CSS.supports(\"padding-top\", \"env(safe-area-inset-top)\");\n }\n\n // Fallback: Check for iPhone X+ user agents\n const ua = navigator.userAgent;\n return (\n /iPhone/.test(ua) &&\n ((screen.width === 375 && screen.height === 812) || // iPhone X, XS, 11 Pro\n (screen.width === 414 && screen.height === 896) || // iPhone XR, XS Max, 11, 11 Pro Max\n (screen.width === 390 && screen.height === 844) || // iPhone 12, 12 Pro, 13, 13 Pro, 14\n (screen.width === 393 && screen.height === 852) || // iPhone 14 Pro\n (screen.width === 428 && screen.height === 926)) // iPhone 12/13/14 Pro Max\n );\n}\n\n/**\n * Get comprehensive safe area styles for a container\n * Combines padding and positioning for complete safe area support\n * \n * @param options - Configuration options\n * @returns React CSSProperties object\n * \n * @example\n * ```typescript\n * const styles = getSafeAreaStyles({\n * applyPadding: ['top', 'bottom'],\n * additionalPadding: 16,\n * position: 'fixed'\n * });\n * ```\n * \n * @public\n * @korean 안전영역스타일얻기\n */\nexport function getSafeAreaStyles(options: {\n readonly applyPadding?: readonly SafeAreaEdge[];\n readonly additionalPadding?: number;\n readonly position?: \"fixed\" | \"absolute\" | \"sticky\";\n}): React.CSSProperties {\n const { applyPadding = [], additionalPadding = 0, position } = options;\n\n const paddingStyles = getSafeAreaPadding(applyPadding, additionalPadding);\n\n return {\n ...paddingStyles,\n ...(position && { position }),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,iBACd,MACA,UACQ;CACR,MAAM,kBACJ,SAAS,QACL,iBAAiB,MACjB,SAAS,WACT,iBAAiB,SACjB,SAAS,SACT,iBAAiB,OACjB,iBAAiB;AAGvB,QAAO,uBAAuB,KAAK,IADb,YAAY,gBACmB;;;;;;;;;;;;;;;;;;;;;;AAuBvD,SAAgB,mBACd,OACA,oBAAoB,GAC2C;CAC/D,MAAM,SACJ,EAAE;AAEJ,MAAK,MAAM,QAAQ,OAAO;EAGxB,MAAM,MAAM,UADT,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE;AAG/C,SAAO,OAAO,OAAO,kBAAkB,MAAM,iBAAiB,KAAK,CAAC;;AAGtE,QAAO"}
1
+ {"version":3,"file":"safeAreaUtils.js","names":[],"sources":["../../src/utils/safeAreaUtils.ts"],"sourcesContent":["/**\n * Safe area utilities for handling notched devices\n * \n * Provides CSS safe area inset helpers for iOS and Android devices\n * with notches, home indicators, and other screen intrusions.\n * \n * @module utils/safeAreaUtils\n * @category UI Utilities\n * @korean 안전영역유틸리티\n */\n\nimport { SAFE_AREA_INSETS } from \"../types/constants/ui\";\n\n/**\n * Safe area edge types\n * \n * @category Safe Area\n * @korean 안전영역가장자리타입\n */\nexport type SafeAreaEdge = \"top\" | \"bottom\" | \"left\" | \"right\";\n\n/**\n * Get CSS safe area inset with fallback\n * Returns CSS `env()` with fallback to constant values\n * \n * @param edge - Which edge to get inset for\n * @param fallback - Optional custom fallback value in pixels\n * @returns CSS string with env() and fallback\n * \n * @example\n * ```typescript\n * getSafeAreaInset('top'); // \"env(safe-area-inset-top, 44px)\"\n * getSafeAreaInset('bottom', 20); // \"env(safe-area-inset-bottom, 20px)\"\n * ```\n * \n * @public\n * @korean 안전영역삽입얻기\n */\nexport function getSafeAreaInset(\n edge: SafeAreaEdge,\n fallback?: number\n): string {\n const defaultFallback =\n edge === \"top\"\n ? SAFE_AREA_INSETS.TOP\n : edge === \"bottom\"\n ? SAFE_AREA_INSETS.BOTTOM\n : edge === \"left\"\n ? SAFE_AREA_INSETS.LEFT\n : SAFE_AREA_INSETS.RIGHT;\n\n const fallbackValue = fallback ?? defaultFallback;\n return `env(safe-area-inset-${edge}, ${fallbackValue}px)`;\n}\n\n/**\n * Get safe area padding for a container\n * Returns CSS padding object with safe area support\n * \n * @param edges - Which edges to apply safe area padding\n * @param additionalPadding - Additional padding to add (in pixels)\n * @returns CSS padding object\n * \n * @example\n * ```typescript\n * getSafeAreaPadding(['top', 'bottom'], 16);\n * // {\n * // paddingTop: \"max(16px, env(safe-area-inset-top))\",\n * // paddingBottom: \"max(16px, env(safe-area-inset-bottom))\"\n * // }\n * ```\n * \n * @public\n * @korean 안전영역패딩얻기\n */\nexport function getSafeAreaPadding(\n edges: readonly SafeAreaEdge[],\n additionalPadding = 0\n): Partial<Record<`padding${Capitalize<SafeAreaEdge>}`, string>> {\n const result: Partial<Record<`padding${Capitalize<SafeAreaEdge>}`, string>> =\n {};\n\n for (const edge of edges) {\n const capitalizedEdge =\n (edge.charAt(0).toUpperCase() + edge.slice(1)) as Capitalize<SafeAreaEdge>;\n const key = `padding${capitalizedEdge}` as const;\n\n result[key] = `max(${additionalPadding}px, ${getSafeAreaInset(edge)})`;\n }\n\n return result;\n}\n\n/**\n * Get safe area aware positioning styles\n * Useful for fixed/absolute positioned elements\n * \n * @param edge - Which edge to position from\n * @param baseOffset - Base offset in pixels\n * @returns CSS positioning string\n * \n * @example\n * ```typescript\n * getSafeAreaPosition('bottom', 20);\n * // \"calc(20px + env(safe-area-inset-bottom, 34px))\"\n * ```\n * \n * @public\n * @korean 안전영역위치얻기\n */\nexport function getSafeAreaPosition(\n edge: SafeAreaEdge,\n baseOffset = 0\n): string {\n return `calc(${baseOffset}px + ${getSafeAreaInset(edge)})`;\n}\n\n/**\n * Get safe area aware height calculation\n * Useful for full-height containers\n * \n * @param excludeEdges - Which edges to exclude from height\n * @returns CSS calc() string for height\n * \n * @example\n * ```typescript\n * getSafeAreaHeight(['top', 'bottom']);\n * // \"calc(100vh - env(safe-area-inset-top, 44px) - env(safe-area-inset-bottom, 34px))\"\n * ```\n * \n * @public\n * @korean 안전영역높이얻기\n */\nexport function getSafeAreaHeight(\n excludeEdges: readonly SafeAreaEdge[]\n): string {\n const insets = excludeEdges.map((edge) => getSafeAreaInset(edge)).join(\" - \");\n return `calc(100vh - ${insets})`;\n}\n\n/**\n * Check if device likely has safe area insets\n * Uses user agent detection (not perfect but useful)\n * \n * @returns Whether device likely has safe area insets\n * \n * @example\n * ```typescript\n * if (hasSafeAreaInsets()) {\n * // Apply safe area styles\n * }\n * ```\n * \n * @public\n * @korean 안전영역삽입여부\n */\nexport function hasSafeAreaInsets(): boolean {\n // Check if CSS env() is supported\n if (typeof CSS !== \"undefined\" && CSS.supports) {\n return CSS.supports(\"padding-top\", \"env(safe-area-inset-top)\");\n }\n\n // Fallback: Check for iPhone X+ user agents\n const ua = navigator.userAgent;\n return (\n /iPhone/.test(ua) &&\n ((screen.width === 375 && screen.height === 812) || // iPhone X, XS, 11 Pro\n (screen.width === 414 && screen.height === 896) || // iPhone XR, XS Max, 11, 11 Pro Max\n (screen.width === 390 && screen.height === 844) || // iPhone 12, 12 Pro, 13, 13 Pro, 14\n (screen.width === 393 && screen.height === 852) || // iPhone 14 Pro\n (screen.width === 428 && screen.height === 926)) // iPhone 12/13/14 Pro Max\n );\n}\n\n/**\n * Get comprehensive safe area styles for a container\n * Combines padding and positioning for complete safe area support\n * \n * @param options - Configuration options\n * @returns React CSSProperties object\n * \n * @example\n * ```typescript\n * const styles = getSafeAreaStyles({\n * applyPadding: ['top', 'bottom'],\n * additionalPadding: 16,\n * position: 'fixed'\n * });\n * ```\n * \n * @public\n * @korean 안전영역스타일얻기\n */\nexport function getSafeAreaStyles(options: {\n readonly applyPadding?: readonly SafeAreaEdge[];\n readonly additionalPadding?: number;\n readonly position?: \"fixed\" | \"absolute\" | \"sticky\";\n}): React.CSSProperties {\n const { applyPadding = [], additionalPadding = 0, position } = options;\n\n const paddingStyles = getSafeAreaPadding(applyPadding, additionalPadding);\n\n return {\n ...paddingStyles,\n ...(position && { position }),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,iBACd,MACA,UACQ;CACR,MAAM,kBACJ,SAAS,QACL,iBAAiB,MACjB,SAAS,WACT,iBAAiB,SACjB,SAAS,SACT,iBAAiB,OACjB,iBAAiB;CAGvB,OAAO,uBAAuB,KAAK,IADb,YAAY,gBACmB;;;;;;;;;;;;;;;;;;;;;;AAuBvD,SAAgB,mBACd,OACA,oBAAoB,GAC2C;CAC/D,MAAM,SACJ,EAAE;CAEJ,KAAK,MAAM,QAAQ,OAAO;EAGxB,MAAM,MAAM,UADT,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE;EAG/C,OAAO,OAAO,OAAO,kBAAkB,MAAM,iBAAiB,KAAK,CAAC;;CAGtE,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"sharedPhysicsConfig.js","names":[],"sources":["../../src/utils/sharedPhysicsConfig.ts"],"sourcesContent":["/**\n * Shared physics configuration for Black Trigram screens.\n *\n * **Korean**: 공유 물리 설정\n *\n * This module provides shared physics configuration utilities to ensure\n * consistent physics behavior, coordinate systems, and camera setup across\n * TrainingScreen3D and CombatScreen3D.\n *\n * ## Synchronization Guarantees\n *\n * All screens using these utilities will have:\n * - Identical camera FOV and positioning for same device types\n * - Consistent arena bounds calculations\n * - Same physics constants (acceleration, stamina regen, etc.)\n * - Unified coordinate system (meter-based)\n * - Matching movement speed and range calculations\n *\n * @module utils/sharedPhysicsConfig\n * @category Physics\n * @korean 공유물리설정\n */\n\nimport {\n BASE_STAMINA_REGEN_RATE,\n BASE_MOVEMENT_ACCELERATION,\n COMBAT_RANGES_METERS,\n} from \"@/types/physicsConstants\";\nimport {\n calculateArenaConfiguration,\n type ArenaConfiguration,\n} from \"./arenaWorldDimensions\";\n\n/**\n * Camera configuration for 3D rendering.\n *\n * **Korean**: 카메라 설정\n *\n * @public\n */\nexport interface CameraConfiguration {\n /** Field of view in degrees */\n readonly fov: number;\n /** Camera position [x, y, z] in meters */\n readonly position: readonly [number, number, number];\n /** Near clipping plane */\n readonly near: number;\n /** Far clipping plane */\n readonly far: number;\n}\n\n/**\n * Complete physics configuration for a game screen.\n *\n * **Korean**: 물리 설정\n *\n * Includes all parameters needed to ensure consistent physics\n * behavior across different screens and device types.\n *\n * @public\n */\nexport interface PhysicsConfiguration {\n /** Arena configuration with pixel and meter dimensions */\n readonly arenaConfig: ArenaConfiguration;\n /** Camera configuration for consistent perspective */\n readonly cameraConfig: CameraConfiguration;\n /** Base stamina regeneration rate (stamina/second) */\n readonly staminaRegenRate: number;\n /** Base movement acceleration (m/s²) */\n readonly movementAcceleration: number;\n /** Combat ranges in meters */\n readonly combatRanges: typeof COMBAT_RANGES_METERS;\n /** Pixels per meter ratio for this configuration */\n readonly pixelsPerMeter: number;\n}\n\n/**\n * Create camera configuration based on device type.\n *\n * Mobile devices get a tighter FOV and closer camera position\n * for better framing of the smaller arena. Desktop gets a wider\n * FOV and further camera for full arena view.\n *\n * **Korean**: 카메라 설정 생성\n *\n * @param isMobile - Whether the device is mobile\n * @returns Camera configuration for device type\n *\n * @example\n * ```typescript\n * const mobile = createCameraConfig(true);\n * // { fov: 55, position: [0, 6, 10], near: 0.1, far: 1000 }\n *\n * const desktop = createCameraConfig(false);\n * // { fov: 60, position: [0, 8, 12], near: 0.1, far: 1000 }\n * ```\n *\n * @public\n */\nexport function createCameraConfig(isMobile: boolean): CameraConfiguration {\n if (isMobile) {\n return {\n fov: 55, // Tighter FOV for smaller mobile arena\n position: [0, 6, 10], // Closer camera\n near: 0.1,\n far: 1000,\n };\n }\n\n return {\n fov: 60, // Standard FOV for desktop\n position: [0, 8, 12], // Further back for full view\n near: 0.1,\n far: 1000,\n };\n}\n\n/**\n * Create complete physics configuration for a screen.\n *\n * This is the primary function for setting up physics in both\n * TrainingScreen3D and CombatScreen3D. It ensures consistent\n * physics behavior by using the same calculations and constants.\n *\n * **Korean**: 물리 설정 생성\n *\n * @param screenWidth - Screen width in pixels\n * @param screenHeight - Screen height in pixels\n * @param topOffset - Pixels reserved at top (HUD, headers)\n * @param bottomOffset - Pixels reserved at bottom (controls, footer)\n * @param isMobile - Whether the device is mobile\n * @returns Complete physics configuration\n *\n * @example\n * ```typescript\n * // Training screen\n * const trainingPhysics = createPhysicsConfig(\n * 1920, 1080, 60, 100, false\n * );\n *\n * // Combat screen\n * const combatPhysics = createPhysicsConfig(\n * 1920, 1080, 60, 100, false\n * );\n *\n * // Both have identical physics configuration\n * trainingPhysics.pixelsPerMeter === combatPhysics.pixelsPerMeter; // true\n * ```\n *\n * @public\n */\nexport function createPhysicsConfig(\n screenWidth: number,\n screenHeight: number,\n topOffset: number,\n bottomOffset: number,\n isMobile: boolean,\n): PhysicsConfiguration {\n // Calculate arena dimensions (same for both screens)\n const arenaConfig = calculateArenaConfiguration(\n screenWidth,\n screenHeight,\n topOffset,\n bottomOffset,\n );\n\n // Create camera config based on device type\n const cameraConfig = createCameraConfig(isMobile);\n\n return {\n arenaConfig,\n cameraConfig,\n staminaRegenRate: BASE_STAMINA_REGEN_RATE,\n movementAcceleration: BASE_MOVEMENT_ACCELERATION,\n combatRanges: COMBAT_RANGES_METERS,\n pixelsPerMeter: arenaConfig.pixelsPerMeter,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmGA,SAAgB,mBAAmB,UAAwC;AACzE,KAAI,SACF,QAAO;EACL,KAAK;EACL,UAAU;GAAC;GAAG;GAAG;GAAG;EACpB,MAAM;EACN,KAAK;EACN;AAGH,QAAO;EACL,KAAK;EACL,UAAU;GAAC;GAAG;GAAG;GAAG;EACpB,MAAM;EACN,KAAK;EACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCH,SAAgB,oBACd,aACA,cACA,WACA,cACA,UACsB;CAEtB,MAAM,cAAc,4BAClB,aACA,cACA,WACA,aACD;AAKD,QAAO;EACL;EACA,cAJmB,mBAAmB,SAItC;EACA,kBAAA;EACA,sBAAA;EACA,cAAc;EACd,gBAAgB,YAAY;EAC7B"}
1
+ {"version":3,"file":"sharedPhysicsConfig.js","names":[],"sources":["../../src/utils/sharedPhysicsConfig.ts"],"sourcesContent":["/**\n * Shared physics configuration for Black Trigram screens.\n *\n * **Korean**: 공유 물리 설정\n *\n * This module provides shared physics configuration utilities to ensure\n * consistent physics behavior, coordinate systems, and camera setup across\n * TrainingScreen3D and CombatScreen3D.\n *\n * ## Synchronization Guarantees\n *\n * All screens using these utilities will have:\n * - Identical camera FOV and positioning for same device types\n * - Consistent arena bounds calculations\n * - Same physics constants (acceleration, stamina regen, etc.)\n * - Unified coordinate system (meter-based)\n * - Matching movement speed and range calculations\n *\n * @module utils/sharedPhysicsConfig\n * @category Physics\n * @korean 공유물리설정\n */\n\nimport {\n BASE_STAMINA_REGEN_RATE,\n BASE_MOVEMENT_ACCELERATION,\n COMBAT_RANGES_METERS,\n} from \"@/types/physicsConstants\";\nimport {\n calculateArenaConfiguration,\n type ArenaConfiguration,\n} from \"./arenaWorldDimensions\";\n\n/**\n * Camera configuration for 3D rendering.\n *\n * **Korean**: 카메라 설정\n *\n * @public\n */\nexport interface CameraConfiguration {\n /** Field of view in degrees */\n readonly fov: number;\n /** Camera position [x, y, z] in meters */\n readonly position: readonly [number, number, number];\n /** Near clipping plane */\n readonly near: number;\n /** Far clipping plane */\n readonly far: number;\n}\n\n/**\n * Complete physics configuration for a game screen.\n *\n * **Korean**: 물리 설정\n *\n * Includes all parameters needed to ensure consistent physics\n * behavior across different screens and device types.\n *\n * @public\n */\nexport interface PhysicsConfiguration {\n /** Arena configuration with pixel and meter dimensions */\n readonly arenaConfig: ArenaConfiguration;\n /** Camera configuration for consistent perspective */\n readonly cameraConfig: CameraConfiguration;\n /** Base stamina regeneration rate (stamina/second) */\n readonly staminaRegenRate: number;\n /** Base movement acceleration (m/s²) */\n readonly movementAcceleration: number;\n /** Combat ranges in meters */\n readonly combatRanges: typeof COMBAT_RANGES_METERS;\n /** Pixels per meter ratio for this configuration */\n readonly pixelsPerMeter: number;\n}\n\n/**\n * Create camera configuration based on device type.\n *\n * Mobile devices get a tighter FOV and closer camera position\n * for better framing of the smaller arena. Desktop gets a wider\n * FOV and further camera for full arena view.\n *\n * **Korean**: 카메라 설정 생성\n *\n * @param isMobile - Whether the device is mobile\n * @returns Camera configuration for device type\n *\n * @example\n * ```typescript\n * const mobile = createCameraConfig(true);\n * // { fov: 55, position: [0, 6, 10], near: 0.1, far: 1000 }\n *\n * const desktop = createCameraConfig(false);\n * // { fov: 60, position: [0, 8, 12], near: 0.1, far: 1000 }\n * ```\n *\n * @public\n */\nexport function createCameraConfig(isMobile: boolean): CameraConfiguration {\n if (isMobile) {\n return {\n fov: 55, // Tighter FOV for smaller mobile arena\n position: [0, 6, 10], // Closer camera\n near: 0.1,\n far: 1000,\n };\n }\n\n return {\n fov: 60, // Standard FOV for desktop\n position: [0, 8, 12], // Further back for full view\n near: 0.1,\n far: 1000,\n };\n}\n\n/**\n * Create complete physics configuration for a screen.\n *\n * This is the primary function for setting up physics in both\n * TrainingScreen3D and CombatScreen3D. It ensures consistent\n * physics behavior by using the same calculations and constants.\n *\n * **Korean**: 물리 설정 생성\n *\n * @param screenWidth - Screen width in pixels\n * @param screenHeight - Screen height in pixels\n * @param topOffset - Pixels reserved at top (HUD, headers)\n * @param bottomOffset - Pixels reserved at bottom (controls, footer)\n * @param isMobile - Whether the device is mobile\n * @returns Complete physics configuration\n *\n * @example\n * ```typescript\n * // Training screen\n * const trainingPhysics = createPhysicsConfig(\n * 1920, 1080, 60, 100, false\n * );\n *\n * // Combat screen\n * const combatPhysics = createPhysicsConfig(\n * 1920, 1080, 60, 100, false\n * );\n *\n * // Both have identical physics configuration\n * trainingPhysics.pixelsPerMeter === combatPhysics.pixelsPerMeter; // true\n * ```\n *\n * @public\n */\nexport function createPhysicsConfig(\n screenWidth: number,\n screenHeight: number,\n topOffset: number,\n bottomOffset: number,\n isMobile: boolean,\n): PhysicsConfiguration {\n // Calculate arena dimensions (same for both screens)\n const arenaConfig = calculateArenaConfiguration(\n screenWidth,\n screenHeight,\n topOffset,\n bottomOffset,\n );\n\n // Create camera config based on device type\n const cameraConfig = createCameraConfig(isMobile);\n\n return {\n arenaConfig,\n cameraConfig,\n staminaRegenRate: BASE_STAMINA_REGEN_RATE,\n movementAcceleration: BASE_MOVEMENT_ACCELERATION,\n combatRanges: COMBAT_RANGES_METERS,\n pixelsPerMeter: arenaConfig.pixelsPerMeter,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmGA,SAAgB,mBAAmB,UAAwC;CACzE,IAAI,UACF,OAAO;EACL,KAAK;EACL,UAAU;GAAC;GAAG;GAAG;GAAG;EACpB,MAAM;EACN,KAAK;EACN;CAGH,OAAO;EACL,KAAK;EACL,UAAU;GAAC;GAAG;GAAG;GAAG;EACpB,MAAM;EACN,KAAK;EACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCH,SAAgB,oBACd,aACA,cACA,WACA,cACA,UACsB;CAEtB,MAAM,cAAc,4BAClB,aACA,cACA,WACA,aACD;CAKD,OAAO;EACL;EACA,cAJmB,mBAAmB,SAItC;EACA,kBAAA;EACA,sBAAA;EACA,cAAc;EACd,gBAAgB,YAAY;EAC7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"skeletonScaling.js","names":[],"sources":["../../src/utils/skeletonScaling.ts"],"sourcesContent":["/**\n * Skeleton rig scaling based on physical attributes.\n *\n * **Korean**: 골격 크기 조정 (Skeleton Scaling)\n *\n * This module provides functions to scale the skeleton rig based on player\n * physical attributes, allowing each archetype to have anatomically accurate\n * body proportions that affect combat hitboxes, vital point positioning, and\n * visual representation.\n *\n * ## Visual Amplification System\n *\n * Raw physical attribute differences between archetypes are subtle (2-12%).\n * To create recognizable visual silhouettes in 3D, this system applies\n * amplification factors that enhance differences while maintaining realistic\n * proportions:\n *\n * - **Limb Scaling**: 2.5x amplification (subtle 5% difference → 12.5% visual)\n * - **Shoulder Width**: 1.15x additional amplification for silhouette distinction\n * - **Overall Height**: 1.5x amplification (kept subtle for realism)\n *\n * ### Expected Visual Silhouettes\n *\n * Each archetype has a distinct, recognizable body shape:\n *\n * - **해커 (Hacker)**: Compact, narrow shoulders (43cm), shortest limbs - SMALLEST\n * - **암살자 (Amsalja)**: Tall (186cm), lean, long limbs (102cm legs) - TALLEST\n * - **정보요원 (Jeongbo)**: Balanced, average proportions - BASELINE\n * - **무사 (Musa)**: Athletic military build (46cm shoulders) - TRADITIONAL\n * - **조직폭력배 (Jojik)**: Massive, widest shoulders (54cm), imposing - LARGEST\n *\n * ## Integration with Korean Anatomy\n *\n * The scaling system respects Korean martial arts anatomy principles:\n * - **Head vital points** (두부 급소) scale with head size\n * - **Neck vulnerability** (경부 취약성) affected by neck length\n * - **Torso vital points** (몸통 급소) distributed across torso length\n * - **Limb reach** (팔다리 거리) determined by arm/leg length\n * - **Shoulder defense** (어깨 방어) coverage from shoulder width\n *\n * @module utils/skeletonScaling\n * @category Combat System\n * @korean 골격조정\n */\n\nimport { PhysicalAttributes } from \"@/types\";\nimport { BoneName } from \"@/types/skeletal\";\nimport {\n amplifyScaling,\n REFERENCE_ATTRIBUTES as BODY_REFERENCE_ATTRIBUTES,\n} from \"../constants/bodyRenderingConstants\";\n\n/**\n * Bone scaling factors for each body region.\n *\n * **Korean**: 뼈 크기 비율 (Bone Scaling Factors)\n *\n * Defines how physical attributes map to skeleton bone scaling.\n * Each factor is a multiplier applied to the base bone length.\n *\n * @public\n * @korean 뼈크기비율\n */\nexport interface BoneScalingFactors {\n /** Head bone scaling (based on headSize) */\n readonly head: number;\n /** Neck bone scaling (based on neckLength) */\n readonly neck: number;\n /** Spine bones scaling (based on torsoLength) */\n readonly spine: number;\n /** Upper arm bones scaling (based on armLength) */\n readonly upperArm: number;\n /** Forearm bones scaling (based on armLength) */\n readonly forearm: number;\n /** Thigh bones scaling (based on legLength) */\n readonly thigh: number;\n /** Shin bones scaling (based on legLength) */\n readonly shin: number;\n /** Shoulder bones scaling (based on shoulderWidth) */\n readonly shoulder: number;\n /** Overall skeleton scaling (based on totalHeight) */\n readonly overall: number;\n}\n\n/**\n * Anatomically correct bone dimensions in centimeters.\n *\n * **Korean**: 해부학적 뼈 치수 (Anatomical Bone Dimensions)\n *\n * These are anatomically accurate bone lengths based on human proportions.\n * For a 178cm reference height, these values create a realistic skeleton.\n *\n * Reference: Human body proportions (8-head canon)\n * - Head: ~12.5% of height (22cm)\n * - Neck: ~5.5% of height (10cm)\n * - Torso: ~33% of height (59cm / 3 segments = 20cm each)\n * - Legs: ~48% of height (85cm total)\n * - Thigh: ~55% of leg length (47cm)\n * - Shin: ~45% of leg length (38cm)\n *\n * @internal\n * @korean 기본뼈치수\n */\nconst BASE_BONE_DIMENSIONS = {\n // Head region\n head: 22, // Head bone length in cm (12.5% of 178cm)\n neck: 10, // Neck bone length in cm (5.5% of 178cm)\n\n // Torso region - Total torso ~59cm for 178cm person (33%)\n spineLower: 20, // Lower spine segment (lumbar)\n spineMiddle: 20, // Middle spine segment (thoracic)\n spineUpper: 19, // Upper spine segment (upper thoracic)\n pelvis: 8, // Pelvis/root bone height (~4.5% of height)\n\n // Arm region (one side) - Total arm ~75cm for 178cm (42% of height)\n shoulder: 12, // Shoulder/clavicle bone (~6.5% of height)\n upperArm: 32, // Upper arm (shoulder to elbow) - ~55% of arm\n forearm: 27, // Forearm (elbow to wrist) - ~45% of arm (including wrist)\n hand: 19, // Hand length (~10.5% of height)\n\n // Leg region (one side) - Total leg ~95cm for 178cm (53% of height)\n hip: 10, // Hip joint height\n thigh: 48, // Thigh (hip to knee) - ~55% of leg length\n shin: 40, // Shin (knee to ankle) - ~45% of leg length\n foot: 5, // Foot/ankle height (ground to ankle)\n\n // Width measurements\n shoulderWidth: 43, // Total shoulder width (half = 21.5cm offset)\n hipWidth: 30, // Total hip width (half = 15cm offset)\n} as const;\n\n/**\n * Reference physical attributes for baseline scaling.\n *\n * **Korean**: 기준 신체 속성 (Reference Physical Attributes)\n *\n * These represent the \"average\" Korean male fighter that the base\n * skeleton rig is designed for. Scaling is calculated relative to\n * these reference values.\n *\n * Uses centralized REFERENCE_ATTRIBUTES from bodyRenderingConstants.\n *\n * @internal\n * @korean 기준신체속성\n */\nconst REFERENCE_ATTRIBUTES: PhysicalAttributes = {\n ...BODY_REFERENCE_ATTRIBUTES,\n};\n\n// VISUAL_AMPLIFICATION_FACTOR and amplifyScaling imported from bodyRenderingConstants\n\n/**\n * Calculate bone scaling factors from physical attributes.\n *\n * **Korean**: 뼈 크기 비율 계산 (Calculate Bone Scaling Factors)\n *\n * Computes scaling multipliers for each bone region based on the\n * player's physical attributes compared to reference values.\n * Differences are amplified for more noticeable visual distinction\n * between archetypes.\n *\n * @param attributes - Player's physical attributes\n * @returns Bone scaling factors for skeleton rig\n *\n * @example\n * ```typescript\n * const factors = calculateBoneScalingFactors(AMSALJA_PHYSICAL);\n * // Amsalja has longer legs: factors.thigh ~= 1.06 (amplified from ~1.03)\n * // Amsalja has smaller head: factors.head ~= 0.90 (amplified from ~0.95)\n * ```\n *\n * @public\n * @korean 뼈크기비율계산\n */\nexport function calculateBoneScalingFactors(\n attributes: PhysicalAttributes,\n): BoneScalingFactors {\n // Calculate raw scaling ratios from physical attributes\n const rawOverall = attributes.totalHeight / REFERENCE_ATTRIBUTES.totalHeight;\n const rawHead = attributes.headSize / REFERENCE_ATTRIBUTES.headSize;\n const rawNeck = attributes.neckLength / REFERENCE_ATTRIBUTES.neckLength;\n const rawSpine = attributes.torsoLength / REFERENCE_ATTRIBUTES.torsoLength;\n const rawArm = attributes.armLength / REFERENCE_ATTRIBUTES.armLength;\n const rawLeg = attributes.legLength / REFERENCE_ATTRIBUTES.legLength;\n const rawShoulder =\n attributes.shoulderWidth / REFERENCE_ATTRIBUTES.shoulderWidth;\n\n // Apply visual amplification for more noticeable archetype differences\n // Overall height scaling is kept subtle (1.5x) for realism\n const overall = 1.0 + (rawOverall - 1.0) * 1.5;\n\n // Head, neck, and body proportions are amplified for visual distinction\n const head = amplifyScaling(rawHead);\n const neck = amplifyScaling(rawNeck);\n const spine = amplifyScaling(rawSpine);\n\n // Limb proportions amplified for noticeable reach differences\n const upperArm = amplifyScaling(rawArm);\n const forearm = amplifyScaling(rawArm);\n const thigh = amplifyScaling(rawLeg);\n const shin = amplifyScaling(rawLeg);\n\n // Shoulder width amplified for body silhouette distinction\n const shoulder = amplifyScaling(rawShoulder);\n\n return {\n head,\n neck,\n spine,\n upperArm,\n forearm,\n thigh,\n shin,\n shoulder,\n overall,\n };\n}\n\n/**\n * Get scaled bone length for a specific bone.\n *\n * **Korean**: 크기 조정된 뼈 길이 (Scaled Bone Length)\n *\n * Returns the scaled length for a specific bone based on the player's\n * physical attributes. Used when creating or updating skeleton rigs.\n *\n * @param boneName - Name of the bone to scale\n * @param attributes - Player's physical attributes\n * @returns Scaled bone length in centimeters\n *\n * @example\n * ```typescript\n * const headLength = getScaledBoneLength(BoneName.HEAD, JOJIK_PHYSICAL);\n * // Jojik has larger head: returns ~23cm (base 20cm * 1.15)\n * ```\n *\n * @public\n * @korean 크기조정된뼈길이\n */\nexport function getScaledBoneLength(\n boneName: string,\n attributes: PhysicalAttributes,\n): number {\n const factors = calculateBoneScalingFactors(attributes);\n\n // Map bone names to their base dimensions and scaling factors\n switch (boneName) {\n // Head region\n case BoneName.HEAD:\n return BASE_BONE_DIMENSIONS.head * factors.head;\n case BoneName.NECK:\n return BASE_BONE_DIMENSIONS.neck * factors.neck;\n\n // Spine region\n case BoneName.SPINE_LOWER:\n return BASE_BONE_DIMENSIONS.spineLower * factors.spine;\n case BoneName.SPINE_MIDDLE:\n return BASE_BONE_DIMENSIONS.spineMiddle * factors.spine;\n case BoneName.SPINE_UPPER:\n return BASE_BONE_DIMENSIONS.spineUpper * factors.spine;\n case BoneName.PELVIS:\n return BASE_BONE_DIMENSIONS.pelvis * factors.overall;\n\n // Left arm\n case BoneName.SHOULDER_L:\n case BoneName.SHOULDER_R:\n return BASE_BONE_DIMENSIONS.shoulder * factors.shoulder;\n case BoneName.UPPER_ARM_L:\n case BoneName.UPPER_ARM_R:\n return BASE_BONE_DIMENSIONS.upperArm * factors.upperArm;\n case BoneName.FOREARM_L:\n case BoneName.FOREARM_R:\n return BASE_BONE_DIMENSIONS.forearm * factors.forearm;\n case BoneName.HAND_L:\n case BoneName.HAND_R:\n return BASE_BONE_DIMENSIONS.hand * factors.overall;\n\n // Left leg\n case BoneName.THIGH_L:\n case BoneName.THIGH_R:\n return BASE_BONE_DIMENSIONS.thigh * factors.thigh;\n case BoneName.SHIN_L:\n case BoneName.SHIN_R:\n return BASE_BONE_DIMENSIONS.shin * factors.shin;\n case BoneName.FOOT_L:\n case BoneName.FOOT_R:\n return BASE_BONE_DIMENSIONS.foot * factors.overall;\n\n // Default: use overall scaling\n default:\n return 10 * factors.overall; // Default bone length\n }\n}\n\n/**\n * Calculate scaled shoulder offset for skeleton positioning.\n *\n * **Korean**: 어깨 오프셋 계산 (Shoulder Offset Calculation)\n *\n * Determines the horizontal offset for shoulder bones based on\n * shoulder width. Used to position arms correctly on the skeleton.\n *\n * Applies amplification to shoulder width for more noticeable silhouette\n * differences between archetypes:\n * - Jojik (54cm): Offset = 31.05cm (54/2 * 1.15) - WIDE, imposing\n * - Hacker (43cm): Offset = 24.73cm (43/2 * 1.15) - NARROW, compact\n * - Difference: 26% wider shoulder span for visual distinction\n *\n * @param attributes - Player's physical attributes\n * @returns Shoulder offset in centimeters (half of total shoulder width, amplified)\n *\n * @example\n * ```typescript\n * const offset = calculateShoulderOffset(JOJIK_PHYSICAL);\n * // Jojik has wide shoulders: returns ~31cm (54cm width / 2 * 1.15)\n * // Left shoulder at -31cm, right shoulder at +31cm\n * ```\n *\n * @public\n * @korean 어깨오프셋계산\n */\nexport function calculateShoulderOffset(\n attributes: PhysicalAttributes,\n): number {\n // Shoulder width is the total span, so divide by 2 for offset from center\n // Apply amplification factor for more visible width differences\n const SHOULDER_AMPLIFICATION = 1.15;\n return (attributes.shoulderWidth / 2) * SHOULDER_AMPLIFICATION;\n}\n\n/**\n * Calculate hitbox dimensions based on physical attributes.\n *\n * **Korean**: 히트박스 크기 계산 (Hitbox Dimension Calculation)\n *\n * Determines the bounding box dimensions for collision detection\n * and hit registration based on body proportions.\n *\n * @param attributes - Player's physical attributes\n * @returns Hitbox dimensions {width, height, depth} in centimeters\n *\n * @example\n * ```typescript\n * const hitbox = calculateHitboxDimensions(AMSALJA_PHYSICAL);\n * // Amsalja is tall and lean: {width: 40, height: 182, depth: 25}\n * ```\n *\n * @public\n * @korean 히트박스크기계산\n */\nexport function calculateHitboxDimensions(attributes: PhysicalAttributes): {\n width: number;\n height: number;\n depth: number;\n} {\n return {\n width: attributes.shoulderWidth,\n height: attributes.totalHeight,\n depth: attributes.shoulderWidth * 0.5, // Approximate depth from shoulder width\n };\n}\n\n/**\n * Calculate vital point offset adjustments based on body proportions.\n *\n * **Korean**: 급소 위치 조정 (Vital Point Position Adjustment)\n *\n * Adjusts vital point positions based on scaled skeleton dimensions.\n * Ensures vital points remain anatomically accurate for different\n * body types while maintaining Korean anatomy principles.\n *\n * @param vitalPointId - ID of the vital point to adjust\n * @param attributes - Player's physical attributes\n * @returns Position adjustment {x, y} offset in centimeters\n *\n * @example\n * ```typescript\n * const adjustment = calculateVitalPointAdjustment(\"head_temple\", MUSA_PHYSICAL);\n * // Returns offset based on head size and torso height\n * ```\n *\n * @public\n * @korean 급소위치조정\n */\nexport function calculateVitalPointAdjustment(\n vitalPointId: string,\n attributes: PhysicalAttributes,\n): { x: number; y: number } {\n const factors = calculateBoneScalingFactors(attributes);\n\n // Determine which body region this vital point belongs to\n // Use exact prefix matching to avoid ambiguity\n if (vitalPointId.startsWith(\"head_\")) {\n // Head vital points scale with head size and torso height\n return {\n x: 0,\n y: (factors.head - 1.0) * 20 + (factors.spine - 1.0) * 60,\n };\n } else if (vitalPointId.startsWith(\"neck_\")) {\n // Neck vital points scale with neck length and torso height\n return {\n x: 0,\n y: (factors.neck - 1.0) * 10 + (factors.spine - 1.0) * 60,\n };\n } else if (vitalPointId.startsWith(\"torso_\")) {\n // Torso vital points scale with torso length\n return {\n x: 0,\n y: (factors.spine - 1.0) * 30,\n };\n } else if (\n vitalPointId.startsWith(\"shoulder_\") ||\n vitalPointId.startsWith(\"arm_\")\n ) {\n // Arm vital points scale with arm length and shoulder width\n return {\n x: (factors.shoulder - 1.0) * 15,\n y: (factors.upperArm - 1.0) * 12,\n };\n } else if (\n vitalPointId.startsWith(\"leg_\") ||\n vitalPointId.startsWith(\"knee_\")\n ) {\n // Leg vital points scale with leg length\n return {\n x: 0,\n y: -(factors.thigh - 1.0) * 15,\n };\n }\n\n // Default: no adjustment\n return { x: 0, y: 0 };\n}\n\n/**\n * Calculate choke effectiveness modifier based on neck dimensions.\n *\n * **Korean**: 목 조르기 효과 계산 (Choke Effectiveness Calculation)\n *\n * Determines how effective chokes and strangles are based on neck\n * length and thickness. Longer, thinner necks are more vulnerable.\n *\n * @param attributes - Player's physical attributes\n * @returns Choke effectiveness multiplier (1.0 = baseline)\n *\n * @example\n * ```typescript\n * const chokeEffectiveness = calculateChokeEffectiveness(AMSALJA_PHYSICAL);\n * // Amsalja has longer neck: returns ~1.1 (10% more vulnerable)\n *\n * const jojikChoke = calculateChokeEffectiveness(JOJIK_PHYSICAL);\n * // Jojik has shorter, thicker neck: returns ~0.9 (10% more resistant)\n * ```\n *\n * @public\n * @korean 목조르기효과계산\n */\nexport function calculateChokeEffectiveness(\n attributes: PhysicalAttributes,\n): number {\n // Longer necks are more vulnerable to chokes\n const lengthFactor = attributes.neckLength / REFERENCE_ATTRIBUTES.neckLength;\n\n // Thicker necks (from muscle/weight) resist chokes better\n const thicknessFactor = REFERENCE_ATTRIBUTES.weight / attributes.weight;\n\n // Combine factors: longer neck + lighter weight = more vulnerable\n return lengthFactor * thicknessFactor;\n}\n\n/**\n * Calculate head strike vulnerability based on head size.\n *\n * **Korean**: 머리 타격 취약성 계산 (Head Strike Vulnerability)\n *\n * Determines vulnerability to head strikes based on head size.\n * Larger heads have more mass and resistance, smaller heads are\n * more vulnerable to concussive force.\n *\n * @param attributes - Player's physical attributes\n * @returns Head strike vulnerability multiplier (1.0 = baseline)\n *\n * @example\n * ```typescript\n * const headVuln = calculateHeadStrikeVulnerability(JOJIK_PHYSICAL);\n * // Jojik has larger head: returns ~0.95 (5% more resistant)\n * ```\n *\n * @public\n * @korean 머리타격취약성계산\n */\nexport function calculateHeadStrikeVulnerability(\n attributes: PhysicalAttributes,\n): number {\n // Larger heads have more mass, providing some protection\n const sizeFactor = REFERENCE_ATTRIBUTES.headSize / attributes.headSize;\n\n // But larger heads are also bigger targets (handled by hitbox size)\n // Here we only calculate the mass-based resistance\n return sizeFactor;\n}\n\n/**\n * Calculate body radius for hit distance calculation.\n *\n * **Korean**: 몸체 반경 계산 (Body Radius Calculation)\n *\n * When calculating hit distance, we measure center-to-center, but attacks\n * land on the target's body surface. This function calculates the effective\n * \"depth\" of a fighter's body from their center point based on their\n * physical attributes.\n *\n * The calculation uses torso depth derived from shoulder width, as broader\n * fighters have proportionally deeper torsos. This is more anatomically\n * accurate than using a fixed constant.\n *\n * Formula: shoulderWidth * 0.5 (torso depth ratio) / 100 (cm to meters)\n *\n * Example body radii:\n * - Hacker (43cm shoulders): 0.215m depth → 0.215m radius\n * - Musa (46cm shoulders): 0.23m depth → 0.23m radius\n * - Jojik (54cm shoulders): 0.27m depth → 0.27m radius\n *\n * @param attributes - Player's physical attributes\n * @returns Body radius in meters (distance from center to body surface)\n *\n * @example\n * ```typescript\n * const radius = calculateBodyRadius(JOJIK_PHYSICAL);\n * // Jojik has wide shoulders (54cm): returns 0.27m\n *\n * const hackerRadius = calculateBodyRadius(HACKER_PHYSICAL);\n * // Hacker is lean (43cm shoulders): returns 0.215m\n * ```\n *\n * @public\n * @korean 몸체반경계산\n */\nexport function calculateBodyRadius(attributes: PhysicalAttributes): number {\n // Body depth is approximately half of shoulder width (anatomical ratio)\n // This matches calculateHitboxDimensions which uses shoulderWidth * 0.5\n const bodyDepthCm = attributes.shoulderWidth * 0.5;\n\n // Convert from centimeters to meters\n // The radius is the distance from center to body surface (half of total depth)\n // Since bodyDepthCm represents front-to-back depth, we use it directly as\n // the distance from center to front surface\n return bodyDepthCm / 100;\n}\n"],"mappings":";CAiJiD,EAC/C,GAAG,wBACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuYD,SAAgB,oBAAoB,YAAwC;AAS1E,QANoB,WAAW,gBAAgB,KAM1B"}
1
+ {"version":3,"file":"skeletonScaling.js","names":[],"sources":["../../src/utils/skeletonScaling.ts"],"sourcesContent":["/**\n * Skeleton rig scaling based on physical attributes.\n *\n * **Korean**: 골격 크기 조정 (Skeleton Scaling)\n *\n * This module provides functions to scale the skeleton rig based on player\n * physical attributes, allowing each archetype to have anatomically accurate\n * body proportions that affect combat hitboxes, vital point positioning, and\n * visual representation.\n *\n * ## Visual Amplification System\n *\n * Raw physical attribute differences between archetypes are subtle (2-12%).\n * To create recognizable visual silhouettes in 3D, this system applies\n * amplification factors that enhance differences while maintaining realistic\n * proportions:\n *\n * - **Limb Scaling**: 2.5x amplification (subtle 5% difference → 12.5% visual)\n * - **Shoulder Width**: 1.15x additional amplification for silhouette distinction\n * - **Overall Height**: 1.5x amplification (kept subtle for realism)\n *\n * ### Expected Visual Silhouettes\n *\n * Each archetype has a distinct, recognizable body shape:\n *\n * - **해커 (Hacker)**: Compact, narrow shoulders (43cm), shortest limbs - SMALLEST\n * - **암살자 (Amsalja)**: Tall (186cm), lean, long limbs (102cm legs) - TALLEST\n * - **정보요원 (Jeongbo)**: Balanced, average proportions - BASELINE\n * - **무사 (Musa)**: Athletic military build (46cm shoulders) - TRADITIONAL\n * - **조직폭력배 (Jojik)**: Massive, widest shoulders (54cm), imposing - LARGEST\n *\n * ## Integration with Korean Anatomy\n *\n * The scaling system respects Korean martial arts anatomy principles:\n * - **Head vital points** (두부 급소) scale with head size\n * - **Neck vulnerability** (경부 취약성) affected by neck length\n * - **Torso vital points** (몸통 급소) distributed across torso length\n * - **Limb reach** (팔다리 거리) determined by arm/leg length\n * - **Shoulder defense** (어깨 방어) coverage from shoulder width\n *\n * @module utils/skeletonScaling\n * @category Combat System\n * @korean 골격조정\n */\n\nimport { PhysicalAttributes } from \"@/types\";\nimport { BoneName } from \"@/types/skeletal\";\nimport {\n amplifyScaling,\n REFERENCE_ATTRIBUTES as BODY_REFERENCE_ATTRIBUTES,\n} from \"../constants/bodyRenderingConstants\";\n\n/**\n * Bone scaling factors for each body region.\n *\n * **Korean**: 뼈 크기 비율 (Bone Scaling Factors)\n *\n * Defines how physical attributes map to skeleton bone scaling.\n * Each factor is a multiplier applied to the base bone length.\n *\n * @public\n * @korean 뼈크기비율\n */\nexport interface BoneScalingFactors {\n /** Head bone scaling (based on headSize) */\n readonly head: number;\n /** Neck bone scaling (based on neckLength) */\n readonly neck: number;\n /** Spine bones scaling (based on torsoLength) */\n readonly spine: number;\n /** Upper arm bones scaling (based on armLength) */\n readonly upperArm: number;\n /** Forearm bones scaling (based on armLength) */\n readonly forearm: number;\n /** Thigh bones scaling (based on legLength) */\n readonly thigh: number;\n /** Shin bones scaling (based on legLength) */\n readonly shin: number;\n /** Shoulder bones scaling (based on shoulderWidth) */\n readonly shoulder: number;\n /** Overall skeleton scaling (based on totalHeight) */\n readonly overall: number;\n}\n\n/**\n * Anatomically correct bone dimensions in centimeters.\n *\n * **Korean**: 해부학적 뼈 치수 (Anatomical Bone Dimensions)\n *\n * These are anatomically accurate bone lengths based on human proportions.\n * For a 178cm reference height, these values create a realistic skeleton.\n *\n * Reference: Human body proportions (8-head canon)\n * - Head: ~12.5% of height (22cm)\n * - Neck: ~5.5% of height (10cm)\n * - Torso: ~33% of height (59cm / 3 segments = 20cm each)\n * - Legs: ~48% of height (85cm total)\n * - Thigh: ~55% of leg length (47cm)\n * - Shin: ~45% of leg length (38cm)\n *\n * @internal\n * @korean 기본뼈치수\n */\nconst BASE_BONE_DIMENSIONS = {\n // Head region\n head: 22, // Head bone length in cm (12.5% of 178cm)\n neck: 10, // Neck bone length in cm (5.5% of 178cm)\n\n // Torso region - Total torso ~59cm for 178cm person (33%)\n spineLower: 20, // Lower spine segment (lumbar)\n spineMiddle: 20, // Middle spine segment (thoracic)\n spineUpper: 19, // Upper spine segment (upper thoracic)\n pelvis: 8, // Pelvis/root bone height (~4.5% of height)\n\n // Arm region (one side) - Total arm ~75cm for 178cm (42% of height)\n shoulder: 12, // Shoulder/clavicle bone (~6.5% of height)\n upperArm: 32, // Upper arm (shoulder to elbow) - ~55% of arm\n forearm: 27, // Forearm (elbow to wrist) - ~45% of arm (including wrist)\n hand: 19, // Hand length (~10.5% of height)\n\n // Leg region (one side) - Total leg ~95cm for 178cm (53% of height)\n hip: 10, // Hip joint height\n thigh: 48, // Thigh (hip to knee) - ~55% of leg length\n shin: 40, // Shin (knee to ankle) - ~45% of leg length\n foot: 5, // Foot/ankle height (ground to ankle)\n\n // Width measurements\n shoulderWidth: 43, // Total shoulder width (half = 21.5cm offset)\n hipWidth: 30, // Total hip width (half = 15cm offset)\n} as const;\n\n/**\n * Reference physical attributes for baseline scaling.\n *\n * **Korean**: 기준 신체 속성 (Reference Physical Attributes)\n *\n * These represent the \"average\" Korean male fighter that the base\n * skeleton rig is designed for. Scaling is calculated relative to\n * these reference values.\n *\n * Uses centralized REFERENCE_ATTRIBUTES from bodyRenderingConstants.\n *\n * @internal\n * @korean 기준신체속성\n */\nconst REFERENCE_ATTRIBUTES: PhysicalAttributes = {\n ...BODY_REFERENCE_ATTRIBUTES,\n};\n\n// VISUAL_AMPLIFICATION_FACTOR and amplifyScaling imported from bodyRenderingConstants\n\n/**\n * Calculate bone scaling factors from physical attributes.\n *\n * **Korean**: 뼈 크기 비율 계산 (Calculate Bone Scaling Factors)\n *\n * Computes scaling multipliers for each bone region based on the\n * player's physical attributes compared to reference values.\n * Differences are amplified for more noticeable visual distinction\n * between archetypes.\n *\n * @param attributes - Player's physical attributes\n * @returns Bone scaling factors for skeleton rig\n *\n * @example\n * ```typescript\n * const factors = calculateBoneScalingFactors(AMSALJA_PHYSICAL);\n * // Amsalja has longer legs: factors.thigh ~= 1.06 (amplified from ~1.03)\n * // Amsalja has smaller head: factors.head ~= 0.90 (amplified from ~0.95)\n * ```\n *\n * @public\n * @korean 뼈크기비율계산\n */\nexport function calculateBoneScalingFactors(\n attributes: PhysicalAttributes,\n): BoneScalingFactors {\n // Calculate raw scaling ratios from physical attributes\n const rawOverall = attributes.totalHeight / REFERENCE_ATTRIBUTES.totalHeight;\n const rawHead = attributes.headSize / REFERENCE_ATTRIBUTES.headSize;\n const rawNeck = attributes.neckLength / REFERENCE_ATTRIBUTES.neckLength;\n const rawSpine = attributes.torsoLength / REFERENCE_ATTRIBUTES.torsoLength;\n const rawArm = attributes.armLength / REFERENCE_ATTRIBUTES.armLength;\n const rawLeg = attributes.legLength / REFERENCE_ATTRIBUTES.legLength;\n const rawShoulder =\n attributes.shoulderWidth / REFERENCE_ATTRIBUTES.shoulderWidth;\n\n // Apply visual amplification for more noticeable archetype differences\n // Overall height scaling is kept subtle (1.5x) for realism\n const overall = 1.0 + (rawOverall - 1.0) * 1.5;\n\n // Head, neck, and body proportions are amplified for visual distinction\n const head = amplifyScaling(rawHead);\n const neck = amplifyScaling(rawNeck);\n const spine = amplifyScaling(rawSpine);\n\n // Limb proportions amplified for noticeable reach differences\n const upperArm = amplifyScaling(rawArm);\n const forearm = amplifyScaling(rawArm);\n const thigh = amplifyScaling(rawLeg);\n const shin = amplifyScaling(rawLeg);\n\n // Shoulder width amplified for body silhouette distinction\n const shoulder = amplifyScaling(rawShoulder);\n\n return {\n head,\n neck,\n spine,\n upperArm,\n forearm,\n thigh,\n shin,\n shoulder,\n overall,\n };\n}\n\n/**\n * Get scaled bone length for a specific bone.\n *\n * **Korean**: 크기 조정된 뼈 길이 (Scaled Bone Length)\n *\n * Returns the scaled length for a specific bone based on the player's\n * physical attributes. Used when creating or updating skeleton rigs.\n *\n * @param boneName - Name of the bone to scale\n * @param attributes - Player's physical attributes\n * @returns Scaled bone length in centimeters\n *\n * @example\n * ```typescript\n * const headLength = getScaledBoneLength(BoneName.HEAD, JOJIK_PHYSICAL);\n * // Jojik has larger head: returns ~23cm (base 20cm * 1.15)\n * ```\n *\n * @public\n * @korean 크기조정된뼈길이\n */\nexport function getScaledBoneLength(\n boneName: string,\n attributes: PhysicalAttributes,\n): number {\n const factors = calculateBoneScalingFactors(attributes);\n\n // Map bone names to their base dimensions and scaling factors\n switch (boneName) {\n // Head region\n case BoneName.HEAD:\n return BASE_BONE_DIMENSIONS.head * factors.head;\n case BoneName.NECK:\n return BASE_BONE_DIMENSIONS.neck * factors.neck;\n\n // Spine region\n case BoneName.SPINE_LOWER:\n return BASE_BONE_DIMENSIONS.spineLower * factors.spine;\n case BoneName.SPINE_MIDDLE:\n return BASE_BONE_DIMENSIONS.spineMiddle * factors.spine;\n case BoneName.SPINE_UPPER:\n return BASE_BONE_DIMENSIONS.spineUpper * factors.spine;\n case BoneName.PELVIS:\n return BASE_BONE_DIMENSIONS.pelvis * factors.overall;\n\n // Left arm\n case BoneName.SHOULDER_L:\n case BoneName.SHOULDER_R:\n return BASE_BONE_DIMENSIONS.shoulder * factors.shoulder;\n case BoneName.UPPER_ARM_L:\n case BoneName.UPPER_ARM_R:\n return BASE_BONE_DIMENSIONS.upperArm * factors.upperArm;\n case BoneName.FOREARM_L:\n case BoneName.FOREARM_R:\n return BASE_BONE_DIMENSIONS.forearm * factors.forearm;\n case BoneName.HAND_L:\n case BoneName.HAND_R:\n return BASE_BONE_DIMENSIONS.hand * factors.overall;\n\n // Left leg\n case BoneName.THIGH_L:\n case BoneName.THIGH_R:\n return BASE_BONE_DIMENSIONS.thigh * factors.thigh;\n case BoneName.SHIN_L:\n case BoneName.SHIN_R:\n return BASE_BONE_DIMENSIONS.shin * factors.shin;\n case BoneName.FOOT_L:\n case BoneName.FOOT_R:\n return BASE_BONE_DIMENSIONS.foot * factors.overall;\n\n // Default: use overall scaling\n default:\n return 10 * factors.overall; // Default bone length\n }\n}\n\n/**\n * Calculate scaled shoulder offset for skeleton positioning.\n *\n * **Korean**: 어깨 오프셋 계산 (Shoulder Offset Calculation)\n *\n * Determines the horizontal offset for shoulder bones based on\n * shoulder width. Used to position arms correctly on the skeleton.\n *\n * Applies amplification to shoulder width for more noticeable silhouette\n * differences between archetypes:\n * - Jojik (54cm): Offset = 31.05cm (54/2 * 1.15) - WIDE, imposing\n * - Hacker (43cm): Offset = 24.73cm (43/2 * 1.15) - NARROW, compact\n * - Difference: 26% wider shoulder span for visual distinction\n *\n * @param attributes - Player's physical attributes\n * @returns Shoulder offset in centimeters (half of total shoulder width, amplified)\n *\n * @example\n * ```typescript\n * const offset = calculateShoulderOffset(JOJIK_PHYSICAL);\n * // Jojik has wide shoulders: returns ~31cm (54cm width / 2 * 1.15)\n * // Left shoulder at -31cm, right shoulder at +31cm\n * ```\n *\n * @public\n * @korean 어깨오프셋계산\n */\nexport function calculateShoulderOffset(\n attributes: PhysicalAttributes,\n): number {\n // Shoulder width is the total span, so divide by 2 for offset from center\n // Apply amplification factor for more visible width differences\n const SHOULDER_AMPLIFICATION = 1.15;\n return (attributes.shoulderWidth / 2) * SHOULDER_AMPLIFICATION;\n}\n\n/**\n * Calculate hitbox dimensions based on physical attributes.\n *\n * **Korean**: 히트박스 크기 계산 (Hitbox Dimension Calculation)\n *\n * Determines the bounding box dimensions for collision detection\n * and hit registration based on body proportions.\n *\n * @param attributes - Player's physical attributes\n * @returns Hitbox dimensions {width, height, depth} in centimeters\n *\n * @example\n * ```typescript\n * const hitbox = calculateHitboxDimensions(AMSALJA_PHYSICAL);\n * // Amsalja is tall and lean: {width: 40, height: 182, depth: 25}\n * ```\n *\n * @public\n * @korean 히트박스크기계산\n */\nexport function calculateHitboxDimensions(attributes: PhysicalAttributes): {\n width: number;\n height: number;\n depth: number;\n} {\n return {\n width: attributes.shoulderWidth,\n height: attributes.totalHeight,\n depth: attributes.shoulderWidth * 0.5, // Approximate depth from shoulder width\n };\n}\n\n/**\n * Calculate vital point offset adjustments based on body proportions.\n *\n * **Korean**: 급소 위치 조정 (Vital Point Position Adjustment)\n *\n * Adjusts vital point positions based on scaled skeleton dimensions.\n * Ensures vital points remain anatomically accurate for different\n * body types while maintaining Korean anatomy principles.\n *\n * @param vitalPointId - ID of the vital point to adjust\n * @param attributes - Player's physical attributes\n * @returns Position adjustment {x, y} offset in centimeters\n *\n * @example\n * ```typescript\n * const adjustment = calculateVitalPointAdjustment(\"head_temple\", MUSA_PHYSICAL);\n * // Returns offset based on head size and torso height\n * ```\n *\n * @public\n * @korean 급소위치조정\n */\nexport function calculateVitalPointAdjustment(\n vitalPointId: string,\n attributes: PhysicalAttributes,\n): { x: number; y: number } {\n const factors = calculateBoneScalingFactors(attributes);\n\n // Determine which body region this vital point belongs to\n // Use exact prefix matching to avoid ambiguity\n if (vitalPointId.startsWith(\"head_\")) {\n // Head vital points scale with head size and torso height\n return {\n x: 0,\n y: (factors.head - 1.0) * 20 + (factors.spine - 1.0) * 60,\n };\n } else if (vitalPointId.startsWith(\"neck_\")) {\n // Neck vital points scale with neck length and torso height\n return {\n x: 0,\n y: (factors.neck - 1.0) * 10 + (factors.spine - 1.0) * 60,\n };\n } else if (vitalPointId.startsWith(\"torso_\")) {\n // Torso vital points scale with torso length\n return {\n x: 0,\n y: (factors.spine - 1.0) * 30,\n };\n } else if (\n vitalPointId.startsWith(\"shoulder_\") ||\n vitalPointId.startsWith(\"arm_\")\n ) {\n // Arm vital points scale with arm length and shoulder width\n return {\n x: (factors.shoulder - 1.0) * 15,\n y: (factors.upperArm - 1.0) * 12,\n };\n } else if (\n vitalPointId.startsWith(\"leg_\") ||\n vitalPointId.startsWith(\"knee_\")\n ) {\n // Leg vital points scale with leg length\n return {\n x: 0,\n y: -(factors.thigh - 1.0) * 15,\n };\n }\n\n // Default: no adjustment\n return { x: 0, y: 0 };\n}\n\n/**\n * Calculate choke effectiveness modifier based on neck dimensions.\n *\n * **Korean**: 목 조르기 효과 계산 (Choke Effectiveness Calculation)\n *\n * Determines how effective chokes and strangles are based on neck\n * length and thickness. Longer, thinner necks are more vulnerable.\n *\n * @param attributes - Player's physical attributes\n * @returns Choke effectiveness multiplier (1.0 = baseline)\n *\n * @example\n * ```typescript\n * const chokeEffectiveness = calculateChokeEffectiveness(AMSALJA_PHYSICAL);\n * // Amsalja has longer neck: returns ~1.1 (10% more vulnerable)\n *\n * const jojikChoke = calculateChokeEffectiveness(JOJIK_PHYSICAL);\n * // Jojik has shorter, thicker neck: returns ~0.9 (10% more resistant)\n * ```\n *\n * @public\n * @korean 목조르기효과계산\n */\nexport function calculateChokeEffectiveness(\n attributes: PhysicalAttributes,\n): number {\n // Longer necks are more vulnerable to chokes\n const lengthFactor = attributes.neckLength / REFERENCE_ATTRIBUTES.neckLength;\n\n // Thicker necks (from muscle/weight) resist chokes better\n const thicknessFactor = REFERENCE_ATTRIBUTES.weight / attributes.weight;\n\n // Combine factors: longer neck + lighter weight = more vulnerable\n return lengthFactor * thicknessFactor;\n}\n\n/**\n * Calculate head strike vulnerability based on head size.\n *\n * **Korean**: 머리 타격 취약성 계산 (Head Strike Vulnerability)\n *\n * Determines vulnerability to head strikes based on head size.\n * Larger heads have more mass and resistance, smaller heads are\n * more vulnerable to concussive force.\n *\n * @param attributes - Player's physical attributes\n * @returns Head strike vulnerability multiplier (1.0 = baseline)\n *\n * @example\n * ```typescript\n * const headVuln = calculateHeadStrikeVulnerability(JOJIK_PHYSICAL);\n * // Jojik has larger head: returns ~0.95 (5% more resistant)\n * ```\n *\n * @public\n * @korean 머리타격취약성계산\n */\nexport function calculateHeadStrikeVulnerability(\n attributes: PhysicalAttributes,\n): number {\n // Larger heads have more mass, providing some protection\n const sizeFactor = REFERENCE_ATTRIBUTES.headSize / attributes.headSize;\n\n // But larger heads are also bigger targets (handled by hitbox size)\n // Here we only calculate the mass-based resistance\n return sizeFactor;\n}\n\n/**\n * Calculate body radius for hit distance calculation.\n *\n * **Korean**: 몸체 반경 계산 (Body Radius Calculation)\n *\n * When calculating hit distance, we measure center-to-center, but attacks\n * land on the target's body surface. This function calculates the effective\n * \"depth\" of a fighter's body from their center point based on their\n * physical attributes.\n *\n * The calculation uses torso depth derived from shoulder width, as broader\n * fighters have proportionally deeper torsos. This is more anatomically\n * accurate than using a fixed constant.\n *\n * Formula: shoulderWidth * 0.5 (torso depth ratio) / 100 (cm to meters)\n *\n * Example body radii:\n * - Hacker (43cm shoulders): 0.215m depth → 0.215m radius\n * - Musa (46cm shoulders): 0.23m depth → 0.23m radius\n * - Jojik (54cm shoulders): 0.27m depth → 0.27m radius\n *\n * @param attributes - Player's physical attributes\n * @returns Body radius in meters (distance from center to body surface)\n *\n * @example\n * ```typescript\n * const radius = calculateBodyRadius(JOJIK_PHYSICAL);\n * // Jojik has wide shoulders (54cm): returns 0.27m\n *\n * const hackerRadius = calculateBodyRadius(HACKER_PHYSICAL);\n * // Hacker is lean (43cm shoulders): returns 0.215m\n * ```\n *\n * @public\n * @korean 몸체반경계산\n */\nexport function calculateBodyRadius(attributes: PhysicalAttributes): number {\n // Body depth is approximately half of shoulder width (anatomical ratio)\n // This matches calculateHitboxDimensions which uses shoulderWidth * 0.5\n const bodyDepthCm = attributes.shoulderWidth * 0.5;\n\n // Convert from centimeters to meters\n // The radius is the distance from center to body surface (half of total depth)\n // Since bodyDepthCm represents front-to-back depth, we use it directly as\n // the distance from center to front surface\n return bodyDepthCm / 100;\n}\n"],"mappings":";CAiJiD,EAC/C,GAAG,wBACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuYD,SAAgB,oBAAoB,YAAwC;CAS1E,OANoB,WAAW,gBAAgB,KAM1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"stanceHelpers.js","names":[],"sources":["../../src/utils/stanceHelpers.ts"],"sourcesContent":["/**\n * Shared utility functions for stance-related operations\n *\n * Provides common helpers for stance colors, names, and symbols\n * to avoid duplication across components.\n *\n * @module utils/stanceHelpers\n * @category Utilities\n * @korean 자세도우미\n */\n\nimport { TrigramStance } from \"../types/common\";\nimport { KOREAN_COLORS } from \"@/types/constants\";\n\n/**\n * Get color for each trigram stance\n * Maps 8 trigrams to Korean cyberpunk color palette\n *\n * @param stance - Current trigram stance\n * @returns Hex color number\n * @korean 자세색상가져오기\n */\nexport const getStanceColor = (stance: TrigramStance): number => {\n const stanceColors = {\n [TrigramStance.GEON]: KOREAN_COLORS.TRIGRAM_GEON_PRIMARY, // Heaven - Gold\n [TrigramStance.TAE]: KOREAN_COLORS.TRIGRAM_TAE_PRIMARY, // Lake - Sky Blue\n [TrigramStance.LI]: KOREAN_COLORS.TRIGRAM_LI_PRIMARY, // Fire - Orange Red\n [TrigramStance.JIN]: KOREAN_COLORS.TRIGRAM_JIN_PRIMARY, // Thunder - Purple\n [TrigramStance.SON]: KOREAN_COLORS.TRIGRAM_SON_PRIMARY, // Wind - Light Green\n [TrigramStance.GAM]: KOREAN_COLORS.TRIGRAM_GAM_PRIMARY, // Water - Blue\n [TrigramStance.GAN]: KOREAN_COLORS.TRIGRAM_GAN_PRIMARY, // Mountain - Brown\n [TrigramStance.GON]: KOREAN_COLORS.TRIGRAM_GON_PRIMARY, // Earth - Dark Khaki\n };\n return stanceColors[stance] ?? KOREAN_COLORS.PRIMARY_CYAN;\n};\n\n/**\n * Get stance display names (Korean + English)\n *\n * @param stance - Current trigram stance\n * @returns Object with korean, english, and romanized names\n * @korean 자세이름가져오기\n */\nexport const getStanceNames = (stance: TrigramStance) => {\n const names = {\n [TrigramStance.GEON]: {\n korean: \"건\",\n english: \"Heaven\",\n romanized: \"Geon\",\n },\n [TrigramStance.TAE]: { korean: \"태\", english: \"Lake\", romanized: \"Tae\" },\n [TrigramStance.LI]: { korean: \"리\", english: \"Fire\", romanized: \"Li\" },\n [TrigramStance.JIN]: { korean: \"진\", english: \"Thunder\", romanized: \"Jin\" },\n [TrigramStance.SON]: { korean: \"손\", english: \"Wind\", romanized: \"Son\" },\n [TrigramStance.GAM]: { korean: \"감\", english: \"Water\", romanized: \"Gam\" },\n [TrigramStance.GAN]: {\n korean: \"간\",\n english: \"Mountain\",\n romanized: \"Gan\",\n },\n [TrigramStance.GON]: { korean: \"곤\", english: \"Earth\", romanized: \"Gon\" },\n };\n return (\n names[stance] ?? { korean: \"건\", english: \"Heaven\", romanized: \"Geon\" }\n );\n};\n\n/**\n * Get trigram Unicode symbol for each stance\n *\n * @param stance - Current trigram stance\n * @returns Unicode trigram symbol\n * @korean 팔괘기호가져오기\n */\nexport const getTrigramSymbol = (stance: TrigramStance): string => {\n const symbols = {\n [TrigramStance.GEON]: \"☰\", // Heaven\n [TrigramStance.TAE]: \"☱\", // Lake\n [TrigramStance.LI]: \"☲\", // Fire\n [TrigramStance.JIN]: \"☳\", // Thunder\n [TrigramStance.SON]: \"☴\", // Wind\n [TrigramStance.GAM]: \"☵\", // Water\n [TrigramStance.GAN]: \"☶\", // Mountain\n [TrigramStance.GON]: \"☷\", // Earth\n };\n return symbols[stance] ?? \"☰\";\n};\n\n/**\n * Get Korean name for each stance\n *\n * @param stance - Current trigram stance\n * @returns Korean name (Hangul)\n * @korean 자세한글이름가져오기\n */\nexport const getStanceKoreanName = (stance: TrigramStance): string => {\n return getStanceNames(stance).korean;\n};\n\n/**\n * Get color as hex string for CSS usage\n *\n * @param stance - Current trigram stance\n * @returns Hex color string (e.g., \"#FFD700\")\n * @korean 자세CSS색상가져오기\n */\nexport const getStanceColorHex = (stance: TrigramStance): string => {\n const color = getStanceColor(stance);\n return `#${color.toString(16).padStart(6, \"0\").toLowerCase()}`;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA,IAAa,kBAAkB,WAAkC;AAW/D,QAAO;GATJ,cAAc,OAAO,cAAc;GACnC,cAAc,MAAM,cAAc;GAClC,cAAc,KAAK,cAAc;GACjC,cAAc,MAAM,cAAc;GAClC,cAAc,MAAM,cAAc;GAClC,cAAc,MAAM,cAAc;GAClC,cAAc,MAAM,cAAc;GAClC,cAAc,MAAM,cAAc;EAE9B,CAAa,WAAW,cAAc;;;;;;;;;AAU/C,IAAa,kBAAkB,WAA0B;AAmBvD,QACE;GAlBC,cAAc,OAAO;GACpB,QAAQ;GACR,SAAS;GACT,WAAW;GACZ;GACA,cAAc,MAAM;GAAE,QAAQ;GAAK,SAAS;GAAQ,WAAW;GAAO;GACtE,cAAc,KAAK;GAAE,QAAQ;GAAK,SAAS;GAAQ,WAAW;GAAM;GACpE,cAAc,MAAM;GAAE,QAAQ;GAAK,SAAS;GAAW,WAAW;GAAO;GACzE,cAAc,MAAM;GAAE,QAAQ;GAAK,SAAS;GAAQ,WAAW;GAAO;GACtE,cAAc,MAAM;GAAE,QAAQ;GAAK,SAAS;GAAS,WAAW;GAAO;GACvE,cAAc,MAAM;GACnB,QAAQ;GACR,SAAS;GACT,WAAW;GACZ;GACA,cAAc,MAAM;GAAE,QAAQ;GAAK,SAAS;GAAS,WAAW;GAAO;EAGxE,CAAM,WAAW;EAAE,QAAQ;EAAK,SAAS;EAAU,WAAW;EAAQ;;;;;;;;;AAW1E,IAAa,oBAAoB,WAAkC;AAWjE,QAAO;GATJ,cAAc,OAAO;GACrB,cAAc,MAAM;GACpB,cAAc,KAAK;GACnB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;EAEhB,CAAQ,WAAW;;;;;;;;;AAU5B,IAAa,uBAAuB,WAAkC;AACpE,QAAO,eAAe,OAAO,CAAC;;;;;;;;;AAUhC,IAAa,qBAAqB,WAAkC;AAElE,QAAO,IADO,eAAe,OAClB,CAAM,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,aAAa"}
1
+ {"version":3,"file":"stanceHelpers.js","names":[],"sources":["../../src/utils/stanceHelpers.ts"],"sourcesContent":["/**\n * Shared utility functions for stance-related operations\n *\n * Provides common helpers for stance colors, names, and symbols\n * to avoid duplication across components.\n *\n * @module utils/stanceHelpers\n * @category Utilities\n * @korean 자세도우미\n */\n\nimport { TrigramStance } from \"../types/common\";\nimport { KOREAN_COLORS } from \"@/types/constants\";\n\n/**\n * Get color for each trigram stance\n * Maps 8 trigrams to Korean cyberpunk color palette\n *\n * @param stance - Current trigram stance\n * @returns Hex color number\n * @korean 자세색상가져오기\n */\nexport const getStanceColor = (stance: TrigramStance): number => {\n const stanceColors = {\n [TrigramStance.GEON]: KOREAN_COLORS.TRIGRAM_GEON_PRIMARY, // Heaven - Gold\n [TrigramStance.TAE]: KOREAN_COLORS.TRIGRAM_TAE_PRIMARY, // Lake - Sky Blue\n [TrigramStance.LI]: KOREAN_COLORS.TRIGRAM_LI_PRIMARY, // Fire - Orange Red\n [TrigramStance.JIN]: KOREAN_COLORS.TRIGRAM_JIN_PRIMARY, // Thunder - Purple\n [TrigramStance.SON]: KOREAN_COLORS.TRIGRAM_SON_PRIMARY, // Wind - Light Green\n [TrigramStance.GAM]: KOREAN_COLORS.TRIGRAM_GAM_PRIMARY, // Water - Blue\n [TrigramStance.GAN]: KOREAN_COLORS.TRIGRAM_GAN_PRIMARY, // Mountain - Brown\n [TrigramStance.GON]: KOREAN_COLORS.TRIGRAM_GON_PRIMARY, // Earth - Dark Khaki\n };\n return stanceColors[stance] ?? KOREAN_COLORS.PRIMARY_CYAN;\n};\n\n/**\n * Get stance display names (Korean + English)\n *\n * @param stance - Current trigram stance\n * @returns Object with korean, english, and romanized names\n * @korean 자세이름가져오기\n */\nexport const getStanceNames = (stance: TrigramStance) => {\n const names = {\n [TrigramStance.GEON]: {\n korean: \"건\",\n english: \"Heaven\",\n romanized: \"Geon\",\n },\n [TrigramStance.TAE]: { korean: \"태\", english: \"Lake\", romanized: \"Tae\" },\n [TrigramStance.LI]: { korean: \"리\", english: \"Fire\", romanized: \"Li\" },\n [TrigramStance.JIN]: { korean: \"진\", english: \"Thunder\", romanized: \"Jin\" },\n [TrigramStance.SON]: { korean: \"손\", english: \"Wind\", romanized: \"Son\" },\n [TrigramStance.GAM]: { korean: \"감\", english: \"Water\", romanized: \"Gam\" },\n [TrigramStance.GAN]: {\n korean: \"간\",\n english: \"Mountain\",\n romanized: \"Gan\",\n },\n [TrigramStance.GON]: { korean: \"곤\", english: \"Earth\", romanized: \"Gon\" },\n };\n return (\n names[stance] ?? { korean: \"건\", english: \"Heaven\", romanized: \"Geon\" }\n );\n};\n\n/**\n * Get trigram Unicode symbol for each stance\n *\n * @param stance - Current trigram stance\n * @returns Unicode trigram symbol\n * @korean 팔괘기호가져오기\n */\nexport const getTrigramSymbol = (stance: TrigramStance): string => {\n const symbols = {\n [TrigramStance.GEON]: \"☰\", // Heaven\n [TrigramStance.TAE]: \"☱\", // Lake\n [TrigramStance.LI]: \"☲\", // Fire\n [TrigramStance.JIN]: \"☳\", // Thunder\n [TrigramStance.SON]: \"☴\", // Wind\n [TrigramStance.GAM]: \"☵\", // Water\n [TrigramStance.GAN]: \"☶\", // Mountain\n [TrigramStance.GON]: \"☷\", // Earth\n };\n return symbols[stance] ?? \"☰\";\n};\n\n/**\n * Get Korean name for each stance\n *\n * @param stance - Current trigram stance\n * @returns Korean name (Hangul)\n * @korean 자세한글이름가져오기\n */\nexport const getStanceKoreanName = (stance: TrigramStance): string => {\n return getStanceNames(stance).korean;\n};\n\n/**\n * Get color as hex string for CSS usage\n *\n * @param stance - Current trigram stance\n * @returns Hex color string (e.g., \"#FFD700\")\n * @korean 자세CSS색상가져오기\n */\nexport const getStanceColorHex = (stance: TrigramStance): string => {\n const color = getStanceColor(stance);\n return `#${color.toString(16).padStart(6, \"0\").toLowerCase()}`;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA,IAAa,kBAAkB,WAAkC;CAW/D,OAAO;GATJ,cAAc,OAAO,cAAc;GACnC,cAAc,MAAM,cAAc;GAClC,cAAc,KAAK,cAAc;GACjC,cAAc,MAAM,cAAc;GAClC,cAAc,MAAM,cAAc;GAClC,cAAc,MAAM,cAAc;GAClC,cAAc,MAAM,cAAc;GAClC,cAAc,MAAM,cAAc;EAE9B,CAAa,WAAW,cAAc;;;;;;;;;AAU/C,IAAa,kBAAkB,WAA0B;CAmBvD,OACE;GAlBC,cAAc,OAAO;GACpB,QAAQ;GACR,SAAS;GACT,WAAW;GACZ;GACA,cAAc,MAAM;GAAE,QAAQ;GAAK,SAAS;GAAQ,WAAW;GAAO;GACtE,cAAc,KAAK;GAAE,QAAQ;GAAK,SAAS;GAAQ,WAAW;GAAM;GACpE,cAAc,MAAM;GAAE,QAAQ;GAAK,SAAS;GAAW,WAAW;GAAO;GACzE,cAAc,MAAM;GAAE,QAAQ;GAAK,SAAS;GAAQ,WAAW;GAAO;GACtE,cAAc,MAAM;GAAE,QAAQ;GAAK,SAAS;GAAS,WAAW;GAAO;GACvE,cAAc,MAAM;GACnB,QAAQ;GACR,SAAS;GACT,WAAW;GACZ;GACA,cAAc,MAAM;GAAE,QAAQ;GAAK,SAAS;GAAS,WAAW;GAAO;EAGxE,CAAM,WAAW;EAAE,QAAQ;EAAK,SAAS;EAAU,WAAW;EAAQ;;;;;;;;;AAW1E,IAAa,oBAAoB,WAAkC;CAWjE,OAAO;GATJ,cAAc,OAAO;GACrB,cAAc,MAAM;GACpB,cAAc,KAAK;GACnB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;EAEhB,CAAQ,WAAW;;;;;;;;;AAU5B,IAAa,uBAAuB,WAAkC;CACpE,OAAO,eAAe,OAAO,CAAC;;;;;;;;;AAUhC,IAAa,qBAAqB,WAAkC;CAElE,OAAO,IADO,eAAe,OAClB,CAAM,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,aAAa"}
@@ -1 +1 @@
1
- {"version":3,"file":"threeObjectPool.js","names":[],"sources":["../../src/utils/threeObjectPool.ts"],"sourcesContent":["/**\n * Three.js Object Pool for Animation Performance\n * \n * Specialized object pooling for frequently created Three.js objects\n * to reduce garbage collection pressure during animation updates.\n * \n * Target: Reduce ~1,344 object allocations per frame to near-zero.\n * \n * @module utils/threeObjectPool\n * @category Performance\n * @korean Three.js객체풀\n */\n\nimport * as THREE from \"three\";\n\n/**\n * Pool for THREE.Euler objects used in bone rotations\n * \n * Reduces GC pressure from creating new Euler objects every frame.\n * Prewarmed with 200 objects (2 characters × 28 bones × 3.5 keyframes avg)\n * \n * @korean 오일러풀\n */\nclass EulerPool {\n private pool: THREE.Euler[] = [];\n private readonly maxSize: number;\n\n constructor(maxSize = 500) {\n this.maxSize = maxSize;\n }\n\n /**\n * Acquire an Euler object from the pool\n * @returns Pooled or new Euler object (reset to 0,0,0)\n */\n acquire(): THREE.Euler {\n const euler = this.pool.pop();\n if (euler) {\n euler.set(0, 0, 0);\n return euler;\n }\n return new THREE.Euler(0, 0, 0);\n }\n\n /**\n * Return an Euler object to the pool\n * @param euler - Euler object to return\n */\n release(euler: THREE.Euler): void {\n if (this.pool.length < this.maxSize) {\n this.pool.push(euler);\n }\n }\n\n /**\n * Pre-populate pool with objects\n * @param count - Number of objects to create\n */\n prewarm(count: number): void {\n const toCreate = Math.min(count, this.maxSize - this.pool.length);\n for (let i = 0; i < toCreate; i++) {\n this.pool.push(new THREE.Euler(0, 0, 0));\n }\n }\n\n /**\n * Get current pool size\n */\n get size(): number {\n return this.pool.length;\n }\n\n /**\n * Clear all pooled objects\n */\n clear(): void {\n this.pool = [];\n }\n}\n\n/**\n * Pool for THREE.Vector3 objects used in bone positions\n * \n * Reduces GC pressure from creating new Vector3 objects every frame.\n * Prewarmed with 200 objects (2 characters × 28 bones × 3.5 keyframes avg)\n * \n * @korean 벡터3풀\n */\nclass Vector3Pool {\n private pool: THREE.Vector3[] = [];\n private readonly maxSize: number;\n\n constructor(maxSize = 500) {\n this.maxSize = maxSize;\n }\n\n /**\n * Acquire a Vector3 object from the pool\n * @returns Pooled or new Vector3 object (reset to 0,0,0)\n */\n acquire(): THREE.Vector3 {\n const vector = this.pool.pop();\n if (vector) {\n vector.set(0, 0, 0);\n return vector;\n }\n return new THREE.Vector3(0, 0, 0);\n }\n\n /**\n * Return a Vector3 object to the pool\n * @param vector - Vector3 object to return\n */\n release(vector: THREE.Vector3): void {\n if (this.pool.length < this.maxSize) {\n this.pool.push(vector);\n }\n }\n\n /**\n * Pre-populate pool with objects\n * @param count - Number of objects to create\n */\n prewarm(count: number): void {\n const toCreate = Math.min(count, this.maxSize - this.pool.length);\n for (let i = 0; i < toCreate; i++) {\n this.pool.push(new THREE.Vector3(0, 0, 0));\n }\n }\n\n /**\n * Get current pool size\n */\n get size(): number {\n return this.pool.length;\n }\n\n /**\n * Clear all pooled objects\n */\n clear(): void {\n this.pool = [];\n }\n}\n\n/**\n * Pool for THREE.Matrix4 objects used in bone transformations\n * \n * Reduces GC pressure from creating new Matrix4 objects during calculations.\n * Prewarmed with 100 objects (2 characters × 28 bones × 2 temp matrices)\n * \n * @korean 행렬4풀\n */\nclass Matrix4Pool {\n private pool: THREE.Matrix4[] = [];\n private readonly maxSize: number;\n\n constructor(maxSize = 200) {\n this.maxSize = maxSize;\n }\n\n /**\n * Acquire a Matrix4 object from the pool\n * @returns Pooled or new Matrix4 object (reset to identity)\n */\n acquire(): THREE.Matrix4 {\n const matrix = this.pool.pop();\n if (matrix) {\n matrix.identity();\n return matrix;\n }\n return new THREE.Matrix4();\n }\n\n /**\n * Return a Matrix4 object to the pool\n * @param matrix - Matrix4 object to return\n */\n release(matrix: THREE.Matrix4): void {\n if (this.pool.length < this.maxSize) {\n this.pool.push(matrix);\n }\n }\n\n /**\n * Pre-populate pool with objects\n * @param count - Number of objects to create\n */\n prewarm(count: number): void {\n const toCreate = Math.min(count, this.maxSize - this.pool.length);\n for (let i = 0; i < toCreate; i++) {\n this.pool.push(new THREE.Matrix4());\n }\n }\n\n /**\n * Get current pool size\n */\n get size(): number {\n return this.pool.length;\n }\n\n /**\n * Clear all pooled objects\n */\n clear(): void {\n this.pool = [];\n }\n}\n\n/**\n * Pool for THREE.Quaternion objects used in rotations\n * \n * Reduces GC pressure from quaternion operations.\n * Prewarmed with 100 objects (2 characters × 28 bones × 2 temp quaternions)\n * \n * @korean 쿼터니언풀\n */\nclass QuaternionPool {\n private pool: THREE.Quaternion[] = [];\n private readonly maxSize: number;\n\n constructor(maxSize = 200) {\n this.maxSize = maxSize;\n }\n\n /**\n * Acquire a Quaternion object from the pool\n * @returns Pooled or new Quaternion object (reset to identity)\n */\n acquire(): THREE.Quaternion {\n const quat = this.pool.pop();\n if (quat) {\n quat.set(0, 0, 0, 1);\n return quat;\n }\n return new THREE.Quaternion(0, 0, 0, 1);\n }\n\n /**\n * Return a Quaternion object to the pool\n * @param quat - Quaternion object to return\n */\n release(quat: THREE.Quaternion): void {\n if (this.pool.length < this.maxSize) {\n this.pool.push(quat);\n }\n }\n\n /**\n * Pre-populate pool with objects\n * @param count - Number of objects to create\n */\n prewarm(count: number): void {\n const toCreate = Math.min(count, this.maxSize - this.pool.length);\n for (let i = 0; i < toCreate; i++) {\n this.pool.push(new THREE.Quaternion(0, 0, 0, 1));\n }\n }\n\n /**\n * Get current pool size\n */\n get size(): number {\n return this.pool.length;\n }\n\n /**\n * Clear all pooled objects\n */\n clear(): void {\n this.pool = [];\n }\n}\n\n/**\n * Pool for THREE.Color objects used in material and particle effects\n * \n * Reduces GC pressure from creating new Color objects for visual effects,\n * particle systems, and dynamic material updates.\n * Prewarmed with 50 objects for typical combat scenarios.\n * \n * @korean 색상풀\n */\nclass ColorPool {\n private pool: THREE.Color[] = [];\n private readonly maxSize: number;\n\n constructor(maxSize = 100) {\n this.maxSize = maxSize;\n }\n\n /**\n * Acquire a Color object from the pool\n * @returns Pooled or new Color object (reset to white)\n */\n acquire(): THREE.Color {\n const color = this.pool.pop();\n if (color) {\n color.set(0xffffff);\n return color;\n }\n return new THREE.Color(0xffffff);\n }\n\n /**\n * Return a Color object to the pool\n * @param color - Color object to return\n */\n release(color: THREE.Color): void {\n if (this.pool.length < this.maxSize) {\n this.pool.push(color);\n }\n }\n\n /**\n * Pre-populate pool with objects\n * @param count - Number of objects to create\n */\n prewarm(count: number): void {\n const toCreate = Math.min(count, this.maxSize - this.pool.length);\n for (let i = 0; i < toCreate; i++) {\n this.pool.push(new THREE.Color(0xffffff));\n }\n }\n\n /**\n * Get current pool size\n */\n get size(): number {\n return this.pool.length;\n }\n\n /**\n * Clear all pooled objects\n */\n clear(): void {\n this.pool = [];\n }\n}\n\n/**\n * Global Three.js object pools\n * \n * Singleton pools for animation system to reduce GC pressure.\n * Prewarmed during app initialization for optimal performance.\n * \n * @example\n * ```typescript\n * // Acquire temporary Euler for calculation\n * const tempEuler = ThreeObjectPools.euler.acquire();\n * tempEuler.set(x, y, z);\n * // ... use tempEuler ...\n * ThreeObjectPools.euler.release(tempEuler);\n * \n * // Acquire temporary Color for particle effect\n * const tempColor = ThreeObjectPools.color.acquire();\n * tempColor.set(0xff0000);\n * // ... use tempColor ...\n * ThreeObjectPools.color.release(tempColor);\n * \n * // Prewarm pools on app start\n * ThreeObjectPools.prewarmAll();\n * ```\n * \n * @korean Three.js객체풀들\n */\nexport const ThreeObjectPools = {\n /** Euler rotation pool */\n euler: new EulerPool(500),\n \n /** Vector3 position pool */\n vector3: new Vector3Pool(500),\n \n /** Matrix4 transformation pool */\n matrix4: new Matrix4Pool(200),\n \n /** Quaternion rotation pool */\n quaternion: new QuaternionPool(200),\n\n /** Color pool for materials and effects */\n color: new ColorPool(100),\n\n /**\n * Pre-populate all pools with recommended sizes\n * \n * Call this during app initialization for optimal performance.\n * Recommended sizes based on 2 characters with 28 bones each plus effects:\n * - Euler: 200 objects (rotations)\n * - Vector3: 200 objects (positions, velocities)\n * - Matrix4: 100 objects (transformations)\n * - Quaternion: 100 objects (rotations)\n * - Color: 50 objects (particle effects, materials)\n */\n prewarmAll(): void {\n this.euler.prewarm(200);\n this.vector3.prewarm(200);\n this.matrix4.prewarm(100);\n this.quaternion.prewarm(100);\n this.color.prewarm(50);\n },\n\n /**\n * Get current status of all pools\n * @returns Pool status for monitoring\n */\n getStatus(): {\n euler: number;\n vector3: number;\n matrix4: number;\n quaternion: number;\n color: number;\n } {\n return {\n euler: this.euler.size,\n vector3: this.vector3.size,\n matrix4: this.matrix4.size,\n quaternion: this.quaternion.size,\n color: this.color.size,\n };\n },\n\n /**\n * Clear all pools (useful for testing)\n */\n clearAll(): void {\n this.euler.clear();\n this.vector3.clear();\n this.matrix4.clear();\n this.quaternion.clear();\n this.color.clear();\n },\n} as const;\n\n/**\n * Helper to execute a function with temporary Euler objects\n * Automatically acquires and releases Euler objects.\n * \n * @param count - Number of Euler objects needed\n * @param fn - Function that uses the Euler objects\n * @returns Result of the function\n * \n * @example\n * ```typescript\n * const result = withTempEulers(2, ([euler1, euler2]) => {\n * euler1.set(x1, y1, z1);\n * euler2.set(x2, y2, z2);\n * return calculateSomething(euler1, euler2);\n * });\n * ```\n * \n * @korean 임시오일러사용\n */\nexport function withTempEulers<T>(\n count: number,\n fn: (eulers: THREE.Euler[]) => T\n): T {\n const eulers: THREE.Euler[] = [];\n try {\n for (let i = 0; i < count; i++) {\n eulers.push(ThreeObjectPools.euler.acquire());\n }\n return fn(eulers);\n } finally {\n eulers.forEach((euler) => ThreeObjectPools.euler.release(euler));\n }\n}\n\n/**\n * Helper to execute a function with temporary Vector3 objects\n * Automatically acquires and releases Vector3 objects.\n * \n * @param count - Number of Vector3 objects needed\n * @param fn - Function that uses the Vector3 objects\n * @returns Result of the function\n * \n * @korean 임시벡터3사용\n */\nexport function withTempVectors<T>(\n count: number,\n fn: (vectors: THREE.Vector3[]) => T\n): T {\n const vectors: THREE.Vector3[] = [];\n try {\n for (let i = 0; i < count; i++) {\n vectors.push(ThreeObjectPools.vector3.acquire());\n }\n return fn(vectors);\n } finally {\n vectors.forEach((vector) => ThreeObjectPools.vector3.release(vector));\n }\n}\n\n/**\n * Helper to execute a function with temporary Matrix4 objects\n * Automatically acquires and releases Matrix4 objects.\n * \n * @param count - Number of Matrix4 objects needed\n * @param fn - Function that uses the Matrix4 objects\n * @returns Result of the function\n * \n * @korean 임시행렬4사용\n */\nexport function withTempMatrices<T>(\n count: number,\n fn: (matrices: THREE.Matrix4[]) => T\n): T {\n const matrices: THREE.Matrix4[] = [];\n try {\n for (let i = 0; i < count; i++) {\n matrices.push(ThreeObjectPools.matrix4.acquire());\n }\n return fn(matrices);\n } finally {\n matrices.forEach((matrix) => ThreeObjectPools.matrix4.release(matrix));\n }\n}\n\n/**\n * Helper to execute a function with temporary Color objects\n * Automatically acquires and releases Color objects.\n * \n * @param count - Number of Color objects needed\n * @param fn - Function that uses the Color objects\n * @returns Result of the function\n * \n * @example\n * ```typescript\n * const avgColor = withTempColors(2, ([color1, color2]) => {\n * color1.set(0xff0000);\n * color2.set(0x0000ff);\n * return color1.lerp(color2, 0.5);\n * });\n * ```\n * \n * @korean 임시색상사용\n */\nexport function withTempColors<T>(\n count: number,\n fn: (colors: THREE.Color[]) => T\n): T {\n const colors: THREE.Color[] = [];\n try {\n for (let i = 0; i < count; i++) {\n colors.push(ThreeObjectPools.color.acquire());\n }\n return fn(colors);\n } finally {\n colors.forEach((color) => ThreeObjectPools.color.release(color));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"threeObjectPool.js","names":[],"sources":["../../src/utils/threeObjectPool.ts"],"sourcesContent":["/**\n * Three.js Object Pool for Animation Performance\n * \n * Specialized object pooling for frequently created Three.js objects\n * to reduce garbage collection pressure during animation updates.\n * \n * Target: Reduce ~1,344 object allocations per frame to near-zero.\n * \n * @module utils/threeObjectPool\n * @category Performance\n * @korean Three.js객체풀\n */\n\nimport * as THREE from \"three\";\n\n/**\n * Pool for THREE.Euler objects used in bone rotations\n * \n * Reduces GC pressure from creating new Euler objects every frame.\n * Prewarmed with 200 objects (2 characters × 28 bones × 3.5 keyframes avg)\n * \n * @korean 오일러풀\n */\nclass EulerPool {\n private pool: THREE.Euler[] = [];\n private readonly maxSize: number;\n\n constructor(maxSize = 500) {\n this.maxSize = maxSize;\n }\n\n /**\n * Acquire an Euler object from the pool\n * @returns Pooled or new Euler object (reset to 0,0,0)\n */\n acquire(): THREE.Euler {\n const euler = this.pool.pop();\n if (euler) {\n euler.set(0, 0, 0);\n return euler;\n }\n return new THREE.Euler(0, 0, 0);\n }\n\n /**\n * Return an Euler object to the pool\n * @param euler - Euler object to return\n */\n release(euler: THREE.Euler): void {\n if (this.pool.length < this.maxSize) {\n this.pool.push(euler);\n }\n }\n\n /**\n * Pre-populate pool with objects\n * @param count - Number of objects to create\n */\n prewarm(count: number): void {\n const toCreate = Math.min(count, this.maxSize - this.pool.length);\n for (let i = 0; i < toCreate; i++) {\n this.pool.push(new THREE.Euler(0, 0, 0));\n }\n }\n\n /**\n * Get current pool size\n */\n get size(): number {\n return this.pool.length;\n }\n\n /**\n * Clear all pooled objects\n */\n clear(): void {\n this.pool = [];\n }\n}\n\n/**\n * Pool for THREE.Vector3 objects used in bone positions\n * \n * Reduces GC pressure from creating new Vector3 objects every frame.\n * Prewarmed with 200 objects (2 characters × 28 bones × 3.5 keyframes avg)\n * \n * @korean 벡터3풀\n */\nclass Vector3Pool {\n private pool: THREE.Vector3[] = [];\n private readonly maxSize: number;\n\n constructor(maxSize = 500) {\n this.maxSize = maxSize;\n }\n\n /**\n * Acquire a Vector3 object from the pool\n * @returns Pooled or new Vector3 object (reset to 0,0,0)\n */\n acquire(): THREE.Vector3 {\n const vector = this.pool.pop();\n if (vector) {\n vector.set(0, 0, 0);\n return vector;\n }\n return new THREE.Vector3(0, 0, 0);\n }\n\n /**\n * Return a Vector3 object to the pool\n * @param vector - Vector3 object to return\n */\n release(vector: THREE.Vector3): void {\n if (this.pool.length < this.maxSize) {\n this.pool.push(vector);\n }\n }\n\n /**\n * Pre-populate pool with objects\n * @param count - Number of objects to create\n */\n prewarm(count: number): void {\n const toCreate = Math.min(count, this.maxSize - this.pool.length);\n for (let i = 0; i < toCreate; i++) {\n this.pool.push(new THREE.Vector3(0, 0, 0));\n }\n }\n\n /**\n * Get current pool size\n */\n get size(): number {\n return this.pool.length;\n }\n\n /**\n * Clear all pooled objects\n */\n clear(): void {\n this.pool = [];\n }\n}\n\n/**\n * Pool for THREE.Matrix4 objects used in bone transformations\n * \n * Reduces GC pressure from creating new Matrix4 objects during calculations.\n * Prewarmed with 100 objects (2 characters × 28 bones × 2 temp matrices)\n * \n * @korean 행렬4풀\n */\nclass Matrix4Pool {\n private pool: THREE.Matrix4[] = [];\n private readonly maxSize: number;\n\n constructor(maxSize = 200) {\n this.maxSize = maxSize;\n }\n\n /**\n * Acquire a Matrix4 object from the pool\n * @returns Pooled or new Matrix4 object (reset to identity)\n */\n acquire(): THREE.Matrix4 {\n const matrix = this.pool.pop();\n if (matrix) {\n matrix.identity();\n return matrix;\n }\n return new THREE.Matrix4();\n }\n\n /**\n * Return a Matrix4 object to the pool\n * @param matrix - Matrix4 object to return\n */\n release(matrix: THREE.Matrix4): void {\n if (this.pool.length < this.maxSize) {\n this.pool.push(matrix);\n }\n }\n\n /**\n * Pre-populate pool with objects\n * @param count - Number of objects to create\n */\n prewarm(count: number): void {\n const toCreate = Math.min(count, this.maxSize - this.pool.length);\n for (let i = 0; i < toCreate; i++) {\n this.pool.push(new THREE.Matrix4());\n }\n }\n\n /**\n * Get current pool size\n */\n get size(): number {\n return this.pool.length;\n }\n\n /**\n * Clear all pooled objects\n */\n clear(): void {\n this.pool = [];\n }\n}\n\n/**\n * Pool for THREE.Quaternion objects used in rotations\n * \n * Reduces GC pressure from quaternion operations.\n * Prewarmed with 100 objects (2 characters × 28 bones × 2 temp quaternions)\n * \n * @korean 쿼터니언풀\n */\nclass QuaternionPool {\n private pool: THREE.Quaternion[] = [];\n private readonly maxSize: number;\n\n constructor(maxSize = 200) {\n this.maxSize = maxSize;\n }\n\n /**\n * Acquire a Quaternion object from the pool\n * @returns Pooled or new Quaternion object (reset to identity)\n */\n acquire(): THREE.Quaternion {\n const quat = this.pool.pop();\n if (quat) {\n quat.set(0, 0, 0, 1);\n return quat;\n }\n return new THREE.Quaternion(0, 0, 0, 1);\n }\n\n /**\n * Return a Quaternion object to the pool\n * @param quat - Quaternion object to return\n */\n release(quat: THREE.Quaternion): void {\n if (this.pool.length < this.maxSize) {\n this.pool.push(quat);\n }\n }\n\n /**\n * Pre-populate pool with objects\n * @param count - Number of objects to create\n */\n prewarm(count: number): void {\n const toCreate = Math.min(count, this.maxSize - this.pool.length);\n for (let i = 0; i < toCreate; i++) {\n this.pool.push(new THREE.Quaternion(0, 0, 0, 1));\n }\n }\n\n /**\n * Get current pool size\n */\n get size(): number {\n return this.pool.length;\n }\n\n /**\n * Clear all pooled objects\n */\n clear(): void {\n this.pool = [];\n }\n}\n\n/**\n * Pool for THREE.Color objects used in material and particle effects\n * \n * Reduces GC pressure from creating new Color objects for visual effects,\n * particle systems, and dynamic material updates.\n * Prewarmed with 50 objects for typical combat scenarios.\n * \n * @korean 색상풀\n */\nclass ColorPool {\n private pool: THREE.Color[] = [];\n private readonly maxSize: number;\n\n constructor(maxSize = 100) {\n this.maxSize = maxSize;\n }\n\n /**\n * Acquire a Color object from the pool\n * @returns Pooled or new Color object (reset to white)\n */\n acquire(): THREE.Color {\n const color = this.pool.pop();\n if (color) {\n color.set(0xffffff);\n return color;\n }\n return new THREE.Color(0xffffff);\n }\n\n /**\n * Return a Color object to the pool\n * @param color - Color object to return\n */\n release(color: THREE.Color): void {\n if (this.pool.length < this.maxSize) {\n this.pool.push(color);\n }\n }\n\n /**\n * Pre-populate pool with objects\n * @param count - Number of objects to create\n */\n prewarm(count: number): void {\n const toCreate = Math.min(count, this.maxSize - this.pool.length);\n for (let i = 0; i < toCreate; i++) {\n this.pool.push(new THREE.Color(0xffffff));\n }\n }\n\n /**\n * Get current pool size\n */\n get size(): number {\n return this.pool.length;\n }\n\n /**\n * Clear all pooled objects\n */\n clear(): void {\n this.pool = [];\n }\n}\n\n/**\n * Global Three.js object pools\n * \n * Singleton pools for animation system to reduce GC pressure.\n * Prewarmed during app initialization for optimal performance.\n * \n * @example\n * ```typescript\n * // Acquire temporary Euler for calculation\n * const tempEuler = ThreeObjectPools.euler.acquire();\n * tempEuler.set(x, y, z);\n * // ... use tempEuler ...\n * ThreeObjectPools.euler.release(tempEuler);\n * \n * // Acquire temporary Color for particle effect\n * const tempColor = ThreeObjectPools.color.acquire();\n * tempColor.set(0xff0000);\n * // ... use tempColor ...\n * ThreeObjectPools.color.release(tempColor);\n * \n * // Prewarm pools on app start\n * ThreeObjectPools.prewarmAll();\n * ```\n * \n * @korean Three.js객체풀들\n */\nexport const ThreeObjectPools = {\n /** Euler rotation pool */\n euler: new EulerPool(500),\n \n /** Vector3 position pool */\n vector3: new Vector3Pool(500),\n \n /** Matrix4 transformation pool */\n matrix4: new Matrix4Pool(200),\n \n /** Quaternion rotation pool */\n quaternion: new QuaternionPool(200),\n\n /** Color pool for materials and effects */\n color: new ColorPool(100),\n\n /**\n * Pre-populate all pools with recommended sizes\n * \n * Call this during app initialization for optimal performance.\n * Recommended sizes based on 2 characters with 28 bones each plus effects:\n * - Euler: 200 objects (rotations)\n * - Vector3: 200 objects (positions, velocities)\n * - Matrix4: 100 objects (transformations)\n * - Quaternion: 100 objects (rotations)\n * - Color: 50 objects (particle effects, materials)\n */\n prewarmAll(): void {\n this.euler.prewarm(200);\n this.vector3.prewarm(200);\n this.matrix4.prewarm(100);\n this.quaternion.prewarm(100);\n this.color.prewarm(50);\n },\n\n /**\n * Get current status of all pools\n * @returns Pool status for monitoring\n */\n getStatus(): {\n euler: number;\n vector3: number;\n matrix4: number;\n quaternion: number;\n color: number;\n } {\n return {\n euler: this.euler.size,\n vector3: this.vector3.size,\n matrix4: this.matrix4.size,\n quaternion: this.quaternion.size,\n color: this.color.size,\n };\n },\n\n /**\n * Clear all pools (useful for testing)\n */\n clearAll(): void {\n this.euler.clear();\n this.vector3.clear();\n this.matrix4.clear();\n this.quaternion.clear();\n this.color.clear();\n },\n} as const;\n\n/**\n * Helper to execute a function with temporary Euler objects\n * Automatically acquires and releases Euler objects.\n * \n * @param count - Number of Euler objects needed\n * @param fn - Function that uses the Euler objects\n * @returns Result of the function\n * \n * @example\n * ```typescript\n * const result = withTempEulers(2, ([euler1, euler2]) => {\n * euler1.set(x1, y1, z1);\n * euler2.set(x2, y2, z2);\n * return calculateSomething(euler1, euler2);\n * });\n * ```\n * \n * @korean 임시오일러사용\n */\nexport function withTempEulers<T>(\n count: number,\n fn: (eulers: THREE.Euler[]) => T\n): T {\n const eulers: THREE.Euler[] = [];\n try {\n for (let i = 0; i < count; i++) {\n eulers.push(ThreeObjectPools.euler.acquire());\n }\n return fn(eulers);\n } finally {\n eulers.forEach((euler) => ThreeObjectPools.euler.release(euler));\n }\n}\n\n/**\n * Helper to execute a function with temporary Vector3 objects\n * Automatically acquires and releases Vector3 objects.\n * \n * @param count - Number of Vector3 objects needed\n * @param fn - Function that uses the Vector3 objects\n * @returns Result of the function\n * \n * @korean 임시벡터3사용\n */\nexport function withTempVectors<T>(\n count: number,\n fn: (vectors: THREE.Vector3[]) => T\n): T {\n const vectors: THREE.Vector3[] = [];\n try {\n for (let i = 0; i < count; i++) {\n vectors.push(ThreeObjectPools.vector3.acquire());\n }\n return fn(vectors);\n } finally {\n vectors.forEach((vector) => ThreeObjectPools.vector3.release(vector));\n }\n}\n\n/**\n * Helper to execute a function with temporary Matrix4 objects\n * Automatically acquires and releases Matrix4 objects.\n * \n * @param count - Number of Matrix4 objects needed\n * @param fn - Function that uses the Matrix4 objects\n * @returns Result of the function\n * \n * @korean 임시행렬4사용\n */\nexport function withTempMatrices<T>(\n count: number,\n fn: (matrices: THREE.Matrix4[]) => T\n): T {\n const matrices: THREE.Matrix4[] = [];\n try {\n for (let i = 0; i < count; i++) {\n matrices.push(ThreeObjectPools.matrix4.acquire());\n }\n return fn(matrices);\n } finally {\n matrices.forEach((matrix) => ThreeObjectPools.matrix4.release(matrix));\n }\n}\n\n/**\n * Helper to execute a function with temporary Color objects\n * Automatically acquires and releases Color objects.\n * \n * @param count - Number of Color objects needed\n * @param fn - Function that uses the Color objects\n * @returns Result of the function\n * \n * @example\n * ```typescript\n * const avgColor = withTempColors(2, ([color1, color2]) => {\n * color1.set(0xff0000);\n * color2.set(0x0000ff);\n * return color1.lerp(color2, 0.5);\n * });\n * ```\n * \n * @korean 임시색상사용\n */\nexport function withTempColors<T>(\n count: number,\n fn: (colors: THREE.Color[]) => T\n): T {\n const colors: THREE.Color[] = [];\n try {\n for (let i = 0; i < count; i++) {\n colors.push(ThreeObjectPools.color.acquire());\n }\n return fn(colors);\n } finally {\n colors.forEach((color) => ThreeObjectPools.color.release(color));\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"visualEffects.js","names":[],"sources":["../../src/utils/visualEffects.ts"],"sourcesContent":["/**\n * Visual Effects Utilities for Korean Cyberpunk Aesthetic\n *\n * Provides advanced visual effects for HTML overlay components including:\n * - Neon glow effects (box-shadow, text-shadow)\n * - Depth effects (layered shadows, gradients, backdrop-blur)\n * - Smooth transitions and animations\n * - Korean font rendering optimization\n * - Hover and focus state generators\n *\n * All effects follow Korean martial arts cyberpunk theme with 60fps performance target.\n *\n * @module utils/visualEffects\n * @category UI Utilities\n * @korean 시각효과유틸리티\n */\n\nimport { KOREAN_COLORS } from \"@/types/constants\";\nimport { hexToRgbaString } from \"./colorUtils\";\n\n/**\n * Neon glow intensity levels\n * @korean 네온글로우강도\n */\nexport type GlowIntensity = \"subtle\" | \"medium\" | \"strong\" | \"intense\";\n\n/**\n * Hover state animation types\n * @korean 호버상태애니메이션\n */\nexport type HoverAnimationType =\n | \"glow\"\n | \"scale\"\n | \"lift\"\n | \"pulse\"\n | \"combined\";\n\n/**\n * Transition timing presets\n * @korean 전환타이밍프리셋\n */\nexport type TransitionTiming = \"fast\" | \"normal\" | \"slow\" | \"smooth\";\n\n/**\n * Depth effect layer configuration\n * @korean 깊이효과레이어설정\n */\nexport interface DepthEffectConfig {\n readonly layers: number;\n readonly baseOffset: number;\n readonly baseBlur: number;\n readonly color: number;\n readonly opacity: number;\n}\n\n/**\n * Get neon glow effect CSS string\n *\n * Creates cyberpunk-style neon glow using box-shadow with Korean colors.\n * Supports multiple intensity levels for different UI elements.\n *\n * @param color - Hex color value from KOREAN_COLORS\n * @param intensity - Glow intensity level\n * @param includeInset - Whether to include inset glow (default: true)\n * @returns CSS box-shadow string with neon glow effect\n *\n * @example\n * ```typescript\n * const glow = getNeonGlowEffect(KOREAN_COLORS.PRIMARY_CYAN, 'medium', true);\n * // Returns: \"0 0 20px rgba(0,230,230,0.6), inset 0 0 10px rgba(0,230,230,0.3)\"\n * // Note: Color conversion is handled by hexToRgbaString utility\n * ```\n *\n * @korean 네온글로우효과얻기\n */\nexport function getNeonGlowEffect(\n color: number,\n intensity: GlowIntensity = \"medium\",\n includeInset: boolean = true,\n): string {\n const glowConfig = {\n subtle: { outer: 10, outerOpacity: 0.4, inner: 5, innerOpacity: 0.2 },\n medium: { outer: 20, outerOpacity: 0.6, inner: 10, innerOpacity: 0.3 },\n strong: { outer: 30, outerOpacity: 0.8, inner: 15, innerOpacity: 0.4 },\n intense: { outer: 40, outerOpacity: 1.0, inner: 20, innerOpacity: 0.5 },\n };\n\n const config = glowConfig[intensity];\n const outerGlow = `0 0 ${config.outer}px ${hexToRgbaString(color, config.outerOpacity)}`;\n const insetGlow = includeInset\n ? `, inset 0 0 ${config.inner}px ${hexToRgbaString(color, config.innerOpacity)}`\n : \"\";\n\n return `${outerGlow}${insetGlow}`;\n}\n\n/**\n * Get neon text shadow effect\n *\n * Creates glowing text effect for Korean and English text using text-shadow.\n * Multiple shadow layers create authentic neon glow appearance.\n *\n * @param color - Hex color value from KOREAN_COLORS\n * @param intensity - Glow intensity level\n * @returns CSS text-shadow string with neon glow\n *\n * @example\n * ```typescript\n * const textGlow = getNeonTextShadow(KOREAN_COLORS.ACCENT_GOLD, 'strong');\n * // Returns: \"0 0 10px rgba(255,196,0,0.8), 0 0 20px rgba(255,196,0,0.6), 0 0 30px rgba(255,196,0,0.4)\"\n * ```\n *\n * @korean 네온텍스트그림자얻기\n */\nexport function getNeonTextShadow(\n color: number,\n intensity: GlowIntensity = \"medium\",\n): string {\n const shadowConfig = {\n subtle: [\n { blur: 5, opacity: 0.6 },\n { blur: 10, opacity: 0.4 },\n ],\n medium: [\n { blur: 10, opacity: 0.8 },\n { blur: 20, opacity: 0.6 },\n { blur: 30, opacity: 0.4 },\n ],\n strong: [\n { blur: 15, opacity: 1.0 },\n { blur: 30, opacity: 0.8 },\n { blur: 45, opacity: 0.6 },\n ],\n intense: [\n { blur: 20, opacity: 1.0 },\n { blur: 40, opacity: 0.9 },\n { blur: 60, opacity: 0.7 },\n { blur: 80, opacity: 0.5 },\n ],\n };\n\n const shadows = shadowConfig[intensity];\n return shadows\n .map(\n ({ blur, opacity }) => `0 0 ${blur}px ${hexToRgbaString(color, opacity)}`,\n )\n .join(\", \");\n}\n\n/**\n * Get layered depth effect\n *\n * Creates depth using multiple shadow layers with increasing offset and blur.\n * Provides 3D-like appearance for Korean-themed UI panels.\n *\n * @param config - Depth effect configuration\n * @returns CSS box-shadow string with layered depth\n *\n * @example\n * ```typescript\n * const depth = getLayeredDepthEffect({\n * layers: 3,\n * baseOffset: 2,\n * baseBlur: 4,\n * color: KOREAN_COLORS.BLACK_SOLID,\n * opacity: 0.5,\n * });\n * ```\n *\n * @korean 레이어깊이효과얻기\n */\nexport function getLayeredDepthEffect(config: DepthEffectConfig): string {\n const { layers, baseOffset, baseBlur, color, opacity } = config;\n const shadows: string[] = [];\n\n for (let i = 1; i <= layers; i++) {\n const offset = baseOffset * i;\n const blur = baseBlur * i;\n const layerOpacity = opacity * (1 - ((i - 1) / layers) * 0.3); // Fade each layer\n shadows.push(\n `0 ${offset}px ${blur}px ${hexToRgbaString(color, layerOpacity)}`,\n );\n }\n\n return shadows.join(\", \");\n}\n\n/**\n * Get cyberpunk gradient background\n *\n * Creates Korean-themed gradient with cyberpunk colors.\n * Supports both linear and radial gradients.\n *\n * @param primaryColor - Primary hex color\n * @param secondaryColor - Secondary hex color\n * @param angle - Gradient angle in degrees (default: 135)\n * @param type - Gradient type ('linear' | 'radial')\n * @returns CSS gradient string\n *\n * @example\n * ```typescript\n * const gradient = getCyberpunkGradient(\n * KOREAN_COLORS.PRIMARY_CYAN,\n * KOREAN_COLORS.UI_BACKGROUND_DARK,\n * 135,\n * 'linear'\n * );\n * ```\n *\n * @korean 사이버펑크그라디언트얻기\n */\nexport function getCyberpunkGradient(\n primaryColor: number,\n secondaryColor: number,\n angle: number = 135,\n type: \"linear\" | \"radial\" = \"linear\",\n): string {\n const color1 = hexToRgbaString(primaryColor, 0.2);\n const color2 = hexToRgbaString(secondaryColor, 0.9);\n\n if (type === \"radial\") {\n return `radial-gradient(circle at top left, ${color1} 0%, ${color2} 100%)`;\n }\n\n return `linear-gradient(${angle}deg, ${color1} 0%, ${color2} 100%)`;\n}\n\n/**\n * Get smooth transition CSS\n *\n * Returns standardized transition string for consistent animations.\n * All transitions target 60fps performance.\n *\n * @param properties - CSS properties to transition (default: 'all')\n * @param timing - Timing preset\n * @returns CSS transition string\n *\n * @example\n * ```typescript\n * const transition = getSmoothTransition('all', 'normal');\n * // Returns: \"all 0.2s ease-in-out\"\n * ```\n *\n * @korean 부드러운전환얻기\n */\nexport function getSmoothTransition(\n properties: string = \"all\",\n timing: TransitionTiming = \"normal\",\n): string {\n const timingConfig = {\n fast: { duration: \"0.15s\", easing: \"ease-out\" },\n normal: { duration: \"0.2s\", easing: \"ease-in-out\" },\n slow: { duration: \"0.3s\", easing: \"ease-in-out\" },\n smooth: { duration: \"0.25s\", easing: \"cubic-bezier(0.4, 0, 0.2, 1)\" },\n };\n\n const config = timingConfig[timing];\n return `${properties} ${config.duration} ${config.easing}`;\n}\n\n/**\n * Get Korean font optimization styles\n *\n * Applies optimal font rendering for Korean characters (Hangul).\n * Includes anti-aliasing, subpixel rendering, and letter spacing.\n *\n * @param fontSize - Font size in pixels\n * @param fontWeight - Font weight (default: 'normal')\n * @returns React.CSSProperties with Korean font optimization\n *\n * @example\n * ```typescript\n * const fontStyle = getKoreanFontOptimization(16, 'bold');\n * // Returns: { fontSize: '16px', WebkitFontSmoothing: 'antialiased', ... }\n * ```\n *\n * @korean 한글폰트최적화얻기\n */\nexport function getKoreanFontOptimization(\n fontSize: number,\n fontWeight: \"normal\" | \"bold\" = \"normal\",\n): React.CSSProperties & {\n WebkitFontSmoothing?: string;\n MozOsxFontSmoothing?: string;\n} {\n return {\n fontSize: `${fontSize}px`,\n fontWeight,\n WebkitFontSmoothing: \"antialiased\",\n MozOsxFontSmoothing: \"grayscale\",\n textRendering: \"optimizeLegibility\",\n letterSpacing: \"0.02em\",\n // Korean characters need slightly more line height\n lineHeight: 1.5,\n };\n}\n\n/**\n * Map numeric glow intensity to GlowIntensity type\n *\n * Helper to standardize glow intensity mapping across components.\n * Converts numeric intensity values to discrete levels.\n *\n * @param glowIntensity - Numeric glow intensity (0-2+ scale)\n * @returns GlowIntensity level (\"subtle\", \"medium\", \"strong\", \"intense\")\n *\n * Mapping thresholds:\n * - intense: >= 1.5\n * - strong: >= 1.0\n * - medium: >= 0.6\n * - subtle: < 0.6\n *\n * @example\n * ```typescript\n * const level = mapGlowIntensityLevel(0.8);\n * // Returns: \"medium\"\n * mapGlowIntensityLevel(2.0);\n * // Returns: \"intense\"\n * ```\n *\n * @korean 광도레벨매핑\n */\nexport function mapGlowIntensityLevel(glowIntensity: number): GlowIntensity {\n if (glowIntensity >= 1.5) return \"intense\";\n if (glowIntensity >= 1.0) return \"strong\";\n if (glowIntensity >= 0.6) return \"medium\";\n return \"subtle\";\n}\n\n/**\n * Get hover state styles with glow effect\n *\n * Generates hover state styles with Korean cyberpunk animations.\n * Includes glow intensification, scale, and lift effects.\n *\n * @param baseColor - Base hex color\n * @param animation - Animation type\n * @param intensity - Glow intensity for hover state\n * @returns React.CSSProperties for hover state\n *\n * @example\n * ```typescript\n * const hoverStyle = getHoverStateStyles(\n * KOREAN_COLORS.PRIMARY_CYAN,\n * 'combined',\n * 'strong'\n * );\n * ```\n *\n * @korean 호버상태스타일얻기\n */\nexport function getHoverStateStyles(\n baseColor: number,\n animation: HoverAnimationType = \"combined\",\n intensity: GlowIntensity = \"strong\",\n): React.CSSProperties {\n const baseStyles: React.CSSProperties = {\n transition: getSmoothTransition(\"all\", \"normal\"),\n cursor: \"pointer\",\n };\n\n const glowStyles: React.CSSProperties = {\n boxShadow: getNeonGlowEffect(baseColor, intensity, true),\n };\n\n const scaleStyles: React.CSSProperties = {\n transform: \"scale(1.05)\",\n };\n\n const liftStyles: React.CSSProperties = {\n transform: \"translateY(-2px)\",\n boxShadow: getLayeredDepthEffect({\n layers: 3,\n baseOffset: 4,\n baseBlur: 8,\n color: KOREAN_COLORS.BLACK_SOLID,\n opacity: 0.6,\n }),\n };\n\n const pulseStyles: React.CSSProperties = {\n animation: \"pulse 1.5s ease-in-out infinite\",\n };\n\n switch (animation) {\n case \"glow\":\n return { ...baseStyles, ...glowStyles };\n case \"scale\":\n return { ...baseStyles, ...scaleStyles };\n case \"lift\":\n return { ...baseStyles, ...liftStyles };\n case \"pulse\":\n return { ...baseStyles, ...pulseStyles };\n case \"combined\":\n return {\n ...baseStyles,\n ...glowStyles,\n transform: \"scale(1.02) translateY(-1px)\",\n };\n default:\n return baseStyles;\n }\n}\n\n/**\n * Get focus state styles with accessibility\n *\n * Creates WCAG-compliant focus indicators with Korean cyberpunk styling.\n * Ensures 2px border with high contrast (4.5:1 minimum).\n *\n * Note: While outline is set to \"none\", the custom 2px border provides\n * equivalent accessibility. For users relying on high-contrast mode or\n * custom browser settings, consider using outline-offset as an additional\n * fallback or ensure the custom border has sufficient contrast (4.5:1+).\n *\n * @param color - Hex color for focus indicator\n * @param includeGlow - Whether to include glow effect (default: true)\n * @returns React.CSSProperties for focus state\n *\n * @example\n * ```typescript\n * const focusStyle = getFocusStateStyles(KOREAN_COLORS.ACCENT_GOLD, true);\n * ```\n *\n * @korean 포커스상태스타일얻기\n */\nexport function getFocusStateStyles(\n color: number,\n includeGlow: boolean = true,\n): React.CSSProperties {\n const baseStyles: React.CSSProperties = {\n outline: \"none\",\n border: `2px solid ${hexToRgbaString(color, 1.0)}`,\n };\n\n if (includeGlow) {\n return {\n ...baseStyles,\n boxShadow: getNeonGlowEffect(color, \"medium\", false),\n };\n }\n\n return baseStyles;\n}\n\n/**\n * Get backdrop blur effect\n *\n * Creates frosted glass effect for Korean-themed overlays.\n * GPU-accelerated for 60fps performance.\n *\n * @param blurAmount - Blur amount in pixels (default: 10)\n * @param saturation - Color saturation multiplier (default: 1.5)\n * @returns React.CSSProperties with backdrop blur\n *\n * @example\n * ```typescript\n * const backdropStyle = getBackdropBlurEffect(12, 1.8);\n * ```\n *\n * @korean 배경블러효과얻기\n */\nexport function getBackdropBlurEffect(\n blurAmount: number = 10,\n saturation: number = 1.5,\n): React.CSSProperties & {\n WebkitBackdropFilter?: string;\n} {\n return {\n backdropFilter: `blur(${blurAmount}px) saturate(${saturation})`,\n WebkitBackdropFilter: `blur(${blurAmount}px) saturate(${saturation})`,\n // Fallback for browsers without backdrop-filter support\n backgroundColor: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.8),\n };\n}\n\n/**\n * Get trigram symbol glow effect\n *\n * Creates glowing effect for Eight Trigram symbols (☰☱☲☳☴☵☶☷).\n * Uses stance-specific colors from KOREAN_COLORS.\n *\n * @param trigramColor - Trigram-specific hex color\n * @param isActive - Whether trigram is currently active\n * @returns React.CSSProperties with trigram glow\n *\n * @example\n * ```typescript\n * const trigramGlow = getTrigramSymbolGlow(\n * KOREAN_COLORS.TRIGRAM_GEON_PRIMARY,\n * true\n * );\n * ```\n *\n * @korean 팔괘상징글로우얻기\n */\nexport function getTrigramSymbolGlow(\n trigramColor: number,\n isActive: boolean,\n): React.CSSProperties {\n const intensity = isActive ? \"intense\" : \"subtle\";\n const scale = isActive ? 1.1 : 1.0;\n\n return {\n color: hexToRgbaString(trigramColor, 1.0),\n textShadow: getNeonTextShadow(trigramColor, intensity),\n transform: `scale(${scale})`,\n transition: getSmoothTransition(\"all\", \"normal\"),\n filter: isActive ? \"brightness(1.2)\" : \"brightness(1.0)\",\n };\n}\n\n/**\n * Get GPU-accelerated animation hint\n *\n * Applies will-change CSS property to hint GPU acceleration.\n * Use sparingly - only for actively animating elements.\n *\n * @param properties - Properties that will animate\n * @returns React.CSSProperties with GPU hints\n *\n * @example\n * ```typescript\n * const gpuHint = getGPUAccelerationHint('transform, opacity');\n * ```\n *\n * @korean GPU가속힌트얻기\n */\nexport function getGPUAccelerationHint(\n properties: string = \"transform\",\n): React.CSSProperties {\n return {\n willChange: properties,\n transform: \"translateZ(0)\", // Force GPU layer\n };\n}\n\n/**\n * Combine multiple shadow effects\n *\n * Merges multiple box-shadow strings into a single CSS value.\n * Useful for combining glow, depth, and custom shadows.\n *\n * @param shadows - Array of box-shadow strings\n * @returns Combined box-shadow CSS string\n *\n * @example\n * ```typescript\n * const combined = combineShadowEffects([\n * getNeonGlowEffect(KOREAN_COLORS.PRIMARY_CYAN, 'medium'),\n * getLayeredDepthEffect({ layers: 2, baseOffset: 2, baseBlur: 4, color: 0x000000, opacity: 0.5 })\n * ]);\n * ```\n *\n * @korean 그림자효과결합\n */\nexport function combineShadowEffects(shadows: string[]): string {\n return shadows.filter(Boolean).join(\", \");\n}\n\n/**\n * Get pulsing animation keyframes\n *\n * Returns CSS keyframe animation for pulsing glow effect.\n * Should be injected into global styles or style tag once.\n * Use unique animation names to avoid conflicts.\n *\n * @param animationName - Unique name for the animation (default: 'pulse')\n * @returns CSS keyframes string\n *\n * @example\n * ```typescript\n * const keyframes = getPulsingKeyframes('pulse-glow');\n * // Inject into global styles once:\n * const style = document.createElement('style');\n * style.innerHTML = keyframes;\n * document.head.appendChild(style);\n * ```\n *\n * @warning SSR and Duplicate Keyframes\n * When using this in Server-Side Rendering (SSR) environments or with multiple\n * component instances, ensure keyframe definitions are registered globally only once.\n * Consider using a global registry or checking for existing keyframe definitions\n * before injection to prevent duplicates and hydration mismatches. Example:\n * ```typescript\n * if (!document.querySelector(`style[data-keyframe=\"${animationName}\"]`)) {\n * const style = document.createElement('style');\n * style.setAttribute('data-keyframe', animationName);\n * style.innerHTML = getPulsingKeyframes(animationName);\n * document.head.appendChild(style);\n * }\n * ```\n *\n * @korean 펄스애니메이션키프레임얻기\n */\nexport function getPulsingKeyframes(animationName: string = \"pulse\"): string {\n return `\n @keyframes ${animationName} {\n 0%, 100% {\n opacity: 1;\n transform: scale(1);\n }\n 50% {\n opacity: 0.8;\n transform: scale(1.05);\n }\n }\n `;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"visualEffects.js","names":[],"sources":["../../src/utils/visualEffects.ts"],"sourcesContent":["/**\n * Visual Effects Utilities for Korean Cyberpunk Aesthetic\n *\n * Provides advanced visual effects for HTML overlay components including:\n * - Neon glow effects (box-shadow, text-shadow)\n * - Depth effects (layered shadows, gradients, backdrop-blur)\n * - Smooth transitions and animations\n * - Korean font rendering optimization\n * - Hover and focus state generators\n *\n * All effects follow Korean martial arts cyberpunk theme with 60fps performance target.\n *\n * @module utils/visualEffects\n * @category UI Utilities\n * @korean 시각효과유틸리티\n */\n\nimport { KOREAN_COLORS } from \"@/types/constants\";\nimport { hexToRgbaString } from \"./colorUtils\";\n\n/**\n * Neon glow intensity levels\n * @korean 네온글로우강도\n */\nexport type GlowIntensity = \"subtle\" | \"medium\" | \"strong\" | \"intense\";\n\n/**\n * Hover state animation types\n * @korean 호버상태애니메이션\n */\nexport type HoverAnimationType =\n | \"glow\"\n | \"scale\"\n | \"lift\"\n | \"pulse\"\n | \"combined\";\n\n/**\n * Transition timing presets\n * @korean 전환타이밍프리셋\n */\nexport type TransitionTiming = \"fast\" | \"normal\" | \"slow\" | \"smooth\";\n\n/**\n * Depth effect layer configuration\n * @korean 깊이효과레이어설정\n */\nexport interface DepthEffectConfig {\n readonly layers: number;\n readonly baseOffset: number;\n readonly baseBlur: number;\n readonly color: number;\n readonly opacity: number;\n}\n\n/**\n * Get neon glow effect CSS string\n *\n * Creates cyberpunk-style neon glow using box-shadow with Korean colors.\n * Supports multiple intensity levels for different UI elements.\n *\n * @param color - Hex color value from KOREAN_COLORS\n * @param intensity - Glow intensity level\n * @param includeInset - Whether to include inset glow (default: true)\n * @returns CSS box-shadow string with neon glow effect\n *\n * @example\n * ```typescript\n * const glow = getNeonGlowEffect(KOREAN_COLORS.PRIMARY_CYAN, 'medium', true);\n * // Returns: \"0 0 20px rgba(0,230,230,0.6), inset 0 0 10px rgba(0,230,230,0.3)\"\n * // Note: Color conversion is handled by hexToRgbaString utility\n * ```\n *\n * @korean 네온글로우효과얻기\n */\nexport function getNeonGlowEffect(\n color: number,\n intensity: GlowIntensity = \"medium\",\n includeInset: boolean = true,\n): string {\n const glowConfig = {\n subtle: { outer: 10, outerOpacity: 0.4, inner: 5, innerOpacity: 0.2 },\n medium: { outer: 20, outerOpacity: 0.6, inner: 10, innerOpacity: 0.3 },\n strong: { outer: 30, outerOpacity: 0.8, inner: 15, innerOpacity: 0.4 },\n intense: { outer: 40, outerOpacity: 1.0, inner: 20, innerOpacity: 0.5 },\n };\n\n const config = glowConfig[intensity];\n const outerGlow = `0 0 ${config.outer}px ${hexToRgbaString(color, config.outerOpacity)}`;\n const insetGlow = includeInset\n ? `, inset 0 0 ${config.inner}px ${hexToRgbaString(color, config.innerOpacity)}`\n : \"\";\n\n return `${outerGlow}${insetGlow}`;\n}\n\n/**\n * Get neon text shadow effect\n *\n * Creates glowing text effect for Korean and English text using text-shadow.\n * Multiple shadow layers create authentic neon glow appearance.\n *\n * @param color - Hex color value from KOREAN_COLORS\n * @param intensity - Glow intensity level\n * @returns CSS text-shadow string with neon glow\n *\n * @example\n * ```typescript\n * const textGlow = getNeonTextShadow(KOREAN_COLORS.ACCENT_GOLD, 'strong');\n * // Returns: \"0 0 10px rgba(255,196,0,0.8), 0 0 20px rgba(255,196,0,0.6), 0 0 30px rgba(255,196,0,0.4)\"\n * ```\n *\n * @korean 네온텍스트그림자얻기\n */\nexport function getNeonTextShadow(\n color: number,\n intensity: GlowIntensity = \"medium\",\n): string {\n const shadowConfig = {\n subtle: [\n { blur: 5, opacity: 0.6 },\n { blur: 10, opacity: 0.4 },\n ],\n medium: [\n { blur: 10, opacity: 0.8 },\n { blur: 20, opacity: 0.6 },\n { blur: 30, opacity: 0.4 },\n ],\n strong: [\n { blur: 15, opacity: 1.0 },\n { blur: 30, opacity: 0.8 },\n { blur: 45, opacity: 0.6 },\n ],\n intense: [\n { blur: 20, opacity: 1.0 },\n { blur: 40, opacity: 0.9 },\n { blur: 60, opacity: 0.7 },\n { blur: 80, opacity: 0.5 },\n ],\n };\n\n const shadows = shadowConfig[intensity];\n return shadows\n .map(\n ({ blur, opacity }) => `0 0 ${blur}px ${hexToRgbaString(color, opacity)}`,\n )\n .join(\", \");\n}\n\n/**\n * Get layered depth effect\n *\n * Creates depth using multiple shadow layers with increasing offset and blur.\n * Provides 3D-like appearance for Korean-themed UI panels.\n *\n * @param config - Depth effect configuration\n * @returns CSS box-shadow string with layered depth\n *\n * @example\n * ```typescript\n * const depth = getLayeredDepthEffect({\n * layers: 3,\n * baseOffset: 2,\n * baseBlur: 4,\n * color: KOREAN_COLORS.BLACK_SOLID,\n * opacity: 0.5,\n * });\n * ```\n *\n * @korean 레이어깊이효과얻기\n */\nexport function getLayeredDepthEffect(config: DepthEffectConfig): string {\n const { layers, baseOffset, baseBlur, color, opacity } = config;\n const shadows: string[] = [];\n\n for (let i = 1; i <= layers; i++) {\n const offset = baseOffset * i;\n const blur = baseBlur * i;\n const layerOpacity = opacity * (1 - ((i - 1) / layers) * 0.3); // Fade each layer\n shadows.push(\n `0 ${offset}px ${blur}px ${hexToRgbaString(color, layerOpacity)}`,\n );\n }\n\n return shadows.join(\", \");\n}\n\n/**\n * Get cyberpunk gradient background\n *\n * Creates Korean-themed gradient with cyberpunk colors.\n * Supports both linear and radial gradients.\n *\n * @param primaryColor - Primary hex color\n * @param secondaryColor - Secondary hex color\n * @param angle - Gradient angle in degrees (default: 135)\n * @param type - Gradient type ('linear' | 'radial')\n * @returns CSS gradient string\n *\n * @example\n * ```typescript\n * const gradient = getCyberpunkGradient(\n * KOREAN_COLORS.PRIMARY_CYAN,\n * KOREAN_COLORS.UI_BACKGROUND_DARK,\n * 135,\n * 'linear'\n * );\n * ```\n *\n * @korean 사이버펑크그라디언트얻기\n */\nexport function getCyberpunkGradient(\n primaryColor: number,\n secondaryColor: number,\n angle: number = 135,\n type: \"linear\" | \"radial\" = \"linear\",\n): string {\n const color1 = hexToRgbaString(primaryColor, 0.2);\n const color2 = hexToRgbaString(secondaryColor, 0.9);\n\n if (type === \"radial\") {\n return `radial-gradient(circle at top left, ${color1} 0%, ${color2} 100%)`;\n }\n\n return `linear-gradient(${angle}deg, ${color1} 0%, ${color2} 100%)`;\n}\n\n/**\n * Get smooth transition CSS\n *\n * Returns standardized transition string for consistent animations.\n * All transitions target 60fps performance.\n *\n * @param properties - CSS properties to transition (default: 'all')\n * @param timing - Timing preset\n * @returns CSS transition string\n *\n * @example\n * ```typescript\n * const transition = getSmoothTransition('all', 'normal');\n * // Returns: \"all 0.2s ease-in-out\"\n * ```\n *\n * @korean 부드러운전환얻기\n */\nexport function getSmoothTransition(\n properties: string = \"all\",\n timing: TransitionTiming = \"normal\",\n): string {\n const timingConfig = {\n fast: { duration: \"0.15s\", easing: \"ease-out\" },\n normal: { duration: \"0.2s\", easing: \"ease-in-out\" },\n slow: { duration: \"0.3s\", easing: \"ease-in-out\" },\n smooth: { duration: \"0.25s\", easing: \"cubic-bezier(0.4, 0, 0.2, 1)\" },\n };\n\n const config = timingConfig[timing];\n return `${properties} ${config.duration} ${config.easing}`;\n}\n\n/**\n * Get Korean font optimization styles\n *\n * Applies optimal font rendering for Korean characters (Hangul).\n * Includes anti-aliasing, subpixel rendering, and letter spacing.\n *\n * @param fontSize - Font size in pixels\n * @param fontWeight - Font weight (default: 'normal')\n * @returns React.CSSProperties with Korean font optimization\n *\n * @example\n * ```typescript\n * const fontStyle = getKoreanFontOptimization(16, 'bold');\n * // Returns: { fontSize: '16px', WebkitFontSmoothing: 'antialiased', ... }\n * ```\n *\n * @korean 한글폰트최적화얻기\n */\nexport function getKoreanFontOptimization(\n fontSize: number,\n fontWeight: \"normal\" | \"bold\" = \"normal\",\n): React.CSSProperties & {\n WebkitFontSmoothing?: string;\n MozOsxFontSmoothing?: string;\n} {\n return {\n fontSize: `${fontSize}px`,\n fontWeight,\n WebkitFontSmoothing: \"antialiased\",\n MozOsxFontSmoothing: \"grayscale\",\n textRendering: \"optimizeLegibility\",\n letterSpacing: \"0.02em\",\n // Korean characters need slightly more line height\n lineHeight: 1.5,\n };\n}\n\n/**\n * Map numeric glow intensity to GlowIntensity type\n *\n * Helper to standardize glow intensity mapping across components.\n * Converts numeric intensity values to discrete levels.\n *\n * @param glowIntensity - Numeric glow intensity (0-2+ scale)\n * @returns GlowIntensity level (\"subtle\", \"medium\", \"strong\", \"intense\")\n *\n * Mapping thresholds:\n * - intense: >= 1.5\n * - strong: >= 1.0\n * - medium: >= 0.6\n * - subtle: < 0.6\n *\n * @example\n * ```typescript\n * const level = mapGlowIntensityLevel(0.8);\n * // Returns: \"medium\"\n * mapGlowIntensityLevel(2.0);\n * // Returns: \"intense\"\n * ```\n *\n * @korean 광도레벨매핑\n */\nexport function mapGlowIntensityLevel(glowIntensity: number): GlowIntensity {\n if (glowIntensity >= 1.5) return \"intense\";\n if (glowIntensity >= 1.0) return \"strong\";\n if (glowIntensity >= 0.6) return \"medium\";\n return \"subtle\";\n}\n\n/**\n * Get hover state styles with glow effect\n *\n * Generates hover state styles with Korean cyberpunk animations.\n * Includes glow intensification, scale, and lift effects.\n *\n * @param baseColor - Base hex color\n * @param animation - Animation type\n * @param intensity - Glow intensity for hover state\n * @returns React.CSSProperties for hover state\n *\n * @example\n * ```typescript\n * const hoverStyle = getHoverStateStyles(\n * KOREAN_COLORS.PRIMARY_CYAN,\n * 'combined',\n * 'strong'\n * );\n * ```\n *\n * @korean 호버상태스타일얻기\n */\nexport function getHoverStateStyles(\n baseColor: number,\n animation: HoverAnimationType = \"combined\",\n intensity: GlowIntensity = \"strong\",\n): React.CSSProperties {\n const baseStyles: React.CSSProperties = {\n transition: getSmoothTransition(\"all\", \"normal\"),\n cursor: \"pointer\",\n };\n\n const glowStyles: React.CSSProperties = {\n boxShadow: getNeonGlowEffect(baseColor, intensity, true),\n };\n\n const scaleStyles: React.CSSProperties = {\n transform: \"scale(1.05)\",\n };\n\n const liftStyles: React.CSSProperties = {\n transform: \"translateY(-2px)\",\n boxShadow: getLayeredDepthEffect({\n layers: 3,\n baseOffset: 4,\n baseBlur: 8,\n color: KOREAN_COLORS.BLACK_SOLID,\n opacity: 0.6,\n }),\n };\n\n const pulseStyles: React.CSSProperties = {\n animation: \"pulse 1.5s ease-in-out infinite\",\n };\n\n switch (animation) {\n case \"glow\":\n return { ...baseStyles, ...glowStyles };\n case \"scale\":\n return { ...baseStyles, ...scaleStyles };\n case \"lift\":\n return { ...baseStyles, ...liftStyles };\n case \"pulse\":\n return { ...baseStyles, ...pulseStyles };\n case \"combined\":\n return {\n ...baseStyles,\n ...glowStyles,\n transform: \"scale(1.02) translateY(-1px)\",\n };\n default:\n return baseStyles;\n }\n}\n\n/**\n * Get focus state styles with accessibility\n *\n * Creates WCAG-compliant focus indicators with Korean cyberpunk styling.\n * Ensures 2px border with high contrast (4.5:1 minimum).\n *\n * Note: While outline is set to \"none\", the custom 2px border provides\n * equivalent accessibility. For users relying on high-contrast mode or\n * custom browser settings, consider using outline-offset as an additional\n * fallback or ensure the custom border has sufficient contrast (4.5:1+).\n *\n * @param color - Hex color for focus indicator\n * @param includeGlow - Whether to include glow effect (default: true)\n * @returns React.CSSProperties for focus state\n *\n * @example\n * ```typescript\n * const focusStyle = getFocusStateStyles(KOREAN_COLORS.ACCENT_GOLD, true);\n * ```\n *\n * @korean 포커스상태스타일얻기\n */\nexport function getFocusStateStyles(\n color: number,\n includeGlow: boolean = true,\n): React.CSSProperties {\n const baseStyles: React.CSSProperties = {\n outline: \"none\",\n border: `2px solid ${hexToRgbaString(color, 1.0)}`,\n };\n\n if (includeGlow) {\n return {\n ...baseStyles,\n boxShadow: getNeonGlowEffect(color, \"medium\", false),\n };\n }\n\n return baseStyles;\n}\n\n/**\n * Get backdrop blur effect\n *\n * Creates frosted glass effect for Korean-themed overlays.\n * GPU-accelerated for 60fps performance.\n *\n * @param blurAmount - Blur amount in pixels (default: 10)\n * @param saturation - Color saturation multiplier (default: 1.5)\n * @returns React.CSSProperties with backdrop blur\n *\n * @example\n * ```typescript\n * const backdropStyle = getBackdropBlurEffect(12, 1.8);\n * ```\n *\n * @korean 배경블러효과얻기\n */\nexport function getBackdropBlurEffect(\n blurAmount: number = 10,\n saturation: number = 1.5,\n): React.CSSProperties & {\n WebkitBackdropFilter?: string;\n} {\n return {\n backdropFilter: `blur(${blurAmount}px) saturate(${saturation})`,\n WebkitBackdropFilter: `blur(${blurAmount}px) saturate(${saturation})`,\n // Fallback for browsers without backdrop-filter support\n backgroundColor: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.8),\n };\n}\n\n/**\n * Get trigram symbol glow effect\n *\n * Creates glowing effect for Eight Trigram symbols (☰☱☲☳☴☵☶☷).\n * Uses stance-specific colors from KOREAN_COLORS.\n *\n * @param trigramColor - Trigram-specific hex color\n * @param isActive - Whether trigram is currently active\n * @returns React.CSSProperties with trigram glow\n *\n * @example\n * ```typescript\n * const trigramGlow = getTrigramSymbolGlow(\n * KOREAN_COLORS.TRIGRAM_GEON_PRIMARY,\n * true\n * );\n * ```\n *\n * @korean 팔괘상징글로우얻기\n */\nexport function getTrigramSymbolGlow(\n trigramColor: number,\n isActive: boolean,\n): React.CSSProperties {\n const intensity = isActive ? \"intense\" : \"subtle\";\n const scale = isActive ? 1.1 : 1.0;\n\n return {\n color: hexToRgbaString(trigramColor, 1.0),\n textShadow: getNeonTextShadow(trigramColor, intensity),\n transform: `scale(${scale})`,\n transition: getSmoothTransition(\"all\", \"normal\"),\n filter: isActive ? \"brightness(1.2)\" : \"brightness(1.0)\",\n };\n}\n\n/**\n * Get GPU-accelerated animation hint\n *\n * Applies will-change CSS property to hint GPU acceleration.\n * Use sparingly - only for actively animating elements.\n *\n * @param properties - Properties that will animate\n * @returns React.CSSProperties with GPU hints\n *\n * @example\n * ```typescript\n * const gpuHint = getGPUAccelerationHint('transform, opacity');\n * ```\n *\n * @korean GPU가속힌트얻기\n */\nexport function getGPUAccelerationHint(\n properties: string = \"transform\",\n): React.CSSProperties {\n return {\n willChange: properties,\n transform: \"translateZ(0)\", // Force GPU layer\n };\n}\n\n/**\n * Combine multiple shadow effects\n *\n * Merges multiple box-shadow strings into a single CSS value.\n * Useful for combining glow, depth, and custom shadows.\n *\n * @param shadows - Array of box-shadow strings\n * @returns Combined box-shadow CSS string\n *\n * @example\n * ```typescript\n * const combined = combineShadowEffects([\n * getNeonGlowEffect(KOREAN_COLORS.PRIMARY_CYAN, 'medium'),\n * getLayeredDepthEffect({ layers: 2, baseOffset: 2, baseBlur: 4, color: 0x000000, opacity: 0.5 })\n * ]);\n * ```\n *\n * @korean 그림자효과결합\n */\nexport function combineShadowEffects(shadows: string[]): string {\n return shadows.filter(Boolean).join(\", \");\n}\n\n/**\n * Get pulsing animation keyframes\n *\n * Returns CSS keyframe animation for pulsing glow effect.\n * Should be injected into global styles or style tag once.\n * Use unique animation names to avoid conflicts.\n *\n * @param animationName - Unique name for the animation (default: 'pulse')\n * @returns CSS keyframes string\n *\n * @example\n * ```typescript\n * const keyframes = getPulsingKeyframes('pulse-glow');\n * // Inject into global styles once:\n * const style = document.createElement('style');\n * style.innerHTML = keyframes;\n * document.head.appendChild(style);\n * ```\n *\n * @warning SSR and Duplicate Keyframes\n * When using this in Server-Side Rendering (SSR) environments or with multiple\n * component instances, ensure keyframe definitions are registered globally only once.\n * Consider using a global registry or checking for existing keyframe definitions\n * before injection to prevent duplicates and hydration mismatches. Example:\n * ```typescript\n * if (!document.querySelector(`style[data-keyframe=\"${animationName}\"]`)) {\n * const style = document.createElement('style');\n * style.setAttribute('data-keyframe', animationName);\n * style.innerHTML = getPulsingKeyframes(animationName);\n * document.head.appendChild(style);\n * }\n * ```\n *\n * @korean 펄스애니메이션키프레임얻기\n */\nexport function getPulsingKeyframes(animationName: string = \"pulse\"): string {\n return `\n @keyframes ${animationName} {\n 0%, 100% {\n opacity: 1;\n transform: scale(1);\n }\n 50% {\n opacity: 0.8;\n transform: scale(1.05);\n }\n }\n `;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blacktrigram",
3
- "version": "0.7.39",
3
+ "version": "0.7.41",
4
4
  "description": "Black Trigram (흑괘) - Korean Martial Arts Combat Simulator. Reusable game systems, combat mechanics, animation framework, and Korean martial arts data built with React, Three.js, and TypeScript.",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -186,7 +186,7 @@
186
186
  "three": "0.184.0"
187
187
  },
188
188
  "devDependencies": {
189
- "@aws-sdk/client-bedrock-runtime": "3.1043.0",
189
+ "@aws-sdk/client-bedrock-runtime": "3.1045.0",
190
190
  "@eslint/js": "10.0.1",
191
191
  "@react-three/drei": "10.7.7",
192
192
  "@react-three/fiber": "9.6.1",
@@ -195,10 +195,10 @@
195
195
  "@testing-library/jest-dom": "6.9.1",
196
196
  "@testing-library/react": "16.3.2",
197
197
  "@testing-library/user-event": "14.6.1",
198
- "@types/node": "25.6.0",
198
+ "@types/node": "25.6.1",
199
199
  "@types/react": "19.2.14",
200
200
  "@types/react-dom": "19.2.3",
201
- "@types/three": "0.184.0",
201
+ "@types/three": "0.184.1",
202
202
  "@vitejs/plugin-react": "6.0.1",
203
203
  "@vitest/coverage-v8": "4.1.5",
204
204
  "@vitest/ui": "4.1.5",
@@ -214,13 +214,13 @@
214
214
  "globals": "17.6.0",
215
215
  "jest-axe": "10.0.0",
216
216
  "jsdom": "29.1.1",
217
- "knip": "6.12.0",
217
+ "knip": "6.12.1",
218
218
  "license-compliance": "3.0.1",
219
219
  "mocha-junit-reporter": "2.2.1",
220
220
  "mochawesome": "7.1.4",
221
221
  "mochawesome-merge": "5.1.1",
222
222
  "mochawesome-report-generator": "6.3.2",
223
- "openai": "6.36.0",
223
+ "openai": "6.37.0",
224
224
  "playwright": "1.59.1",
225
225
  "postprocessing": "6.39.1",
226
226
  "react": "19.2.6",
@@ -237,7 +237,7 @@
237
237
  "typedoc-plugin-missing-exports": "4.1.3",
238
238
  "typescript": "6.0.3",
239
239
  "typescript-eslint": "8.59.2",
240
- "vite": "8.0.10",
240
+ "vite": "8.0.11",
241
241
  "vite-bundle-analyzer": "1.3.8",
242
242
  "vite-tsconfig-paths": "6.1.1",
243
243
  "vitest": "4.1.5"
@@ -248,6 +248,6 @@
248
248
  },
249
249
  "glob": "13.0.1",
250
250
  "three": "0.184.0",
251
- "@types/three": "0.184.0"
251
+ "@types/three": "0.184.1"
252
252
  }
253
253
  }