blacktrigram 0.7.47 → 0.7.49

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 (471) 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.d.ts.map +1 -1
  14. package/lib/components/screens/combat/CombatScreen3D.js +29 -25
  15. package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
  16. package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
  17. package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
  18. package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
  19. package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
  20. package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
  21. package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
  22. package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
  23. package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
  24. package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
  25. package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
  26. package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
  27. package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
  28. package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
  29. package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
  30. package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
  31. package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
  32. package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
  33. package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
  34. package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
  35. package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
  36. package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
  37. package/lib/components/screens/combat/components/hud/CombatBottomHUD.d.ts.map +1 -1
  38. package/lib/components/screens/combat/components/hud/CombatBottomHUD.js +2 -2
  39. package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
  40. package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
  41. package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js +1 -1
  42. package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
  43. package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
  44. package/lib/components/screens/combat/components/hud/CombatTopHUD.d.ts.map +1 -1
  45. package/lib/components/screens/combat/components/hud/CombatTopHUD.js +2 -1
  46. package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
  47. package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
  48. package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
  49. package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
  50. package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
  51. package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
  52. package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
  53. package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
  54. package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
  55. package/lib/components/screens/combat/helpers/AnimationUpdater.d.ts.map +1 -1
  56. package/lib/components/screens/combat/helpers/AnimationUpdater.js +4 -2
  57. package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
  58. package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
  59. package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
  60. package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
  61. package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
  62. package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
  63. package/lib/components/screens/combat/hooks/useCombatLayout.d.ts.map +1 -1
  64. package/lib/components/screens/combat/hooks/useCombatLayout.js +11 -5
  65. package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
  66. package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
  67. package/lib/components/screens/controls/ControlsScreen3D.js +1 -1
  68. package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
  69. package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
  70. package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
  71. package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
  72. package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
  73. package/lib/components/screens/controls/components/Key3D.js.map +1 -1
  74. package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
  75. package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
  76. package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
  77. package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
  78. package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
  79. package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
  80. package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
  81. package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
  82. package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
  83. package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
  84. package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
  85. package/lib/components/screens/intro/IntroScreen3D.js +1 -1
  86. package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
  87. package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
  88. package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
  89. package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
  90. package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
  91. package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
  92. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
  93. package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
  94. package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
  95. package/lib/components/screens/philosophy/PhilosophyScreen3D.js +1 -1
  96. package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
  97. package/lib/components/screens/training/TrainingScreen3D.d.ts.map +1 -1
  98. package/lib/components/screens/training/TrainingScreen3D.js +3 -11
  99. package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
  100. package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
  101. package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
  102. package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
  103. package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
  104. package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
  105. package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
  106. package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
  107. package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
  108. package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
  109. package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
  110. package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
  111. package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
  112. package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
  113. package/lib/components/screens/training/components/hud/TrainingBottomHUD.d.ts.map +1 -1
  114. package/lib/components/screens/training/components/hud/TrainingBottomHUD.js +2 -2
  115. package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
  116. package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
  117. package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
  118. package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
  119. package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
  120. package/lib/components/screens/training/hooks/useTrainingActions.d.ts +1 -0
  121. package/lib/components/screens/training/hooks/useTrainingActions.d.ts.map +1 -1
  122. package/lib/components/screens/training/hooks/useTrainingActions.js +6 -4
  123. package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
  124. package/lib/components/screens/training/hooks/useTrainingLayout.d.ts.map +1 -1
  125. package/lib/components/screens/training/hooks/useTrainingLayout.js +11 -5
  126. package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
  127. package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
  128. package/lib/components/shared/base/BaseButton.js.map +1 -1
  129. package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
  130. package/lib/components/shared/base/BasePanel.js.map +1 -1
  131. package/lib/components/shared/base/BaseText.js.map +1 -1
  132. package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
  133. package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
  134. package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
  135. package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
  136. package/lib/components/shared/mobile/HapticController.js.map +1 -1
  137. package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
  138. package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
  139. package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
  140. package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
  141. package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
  142. package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
  143. package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
  144. package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
  145. package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
  146. package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
  147. package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
  148. package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
  149. package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
  150. package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
  151. package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
  152. package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
  153. package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
  154. package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
  155. package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
  156. package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
  157. package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
  158. package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
  159. package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
  160. package/lib/components/shared/three/models/SkeletalPlayer3D.d.ts.map +1 -1
  161. package/lib/components/shared/three/models/SkeletalPlayer3D.js +7 -5
  162. package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
  163. package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
  164. package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
  165. package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
  166. package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
  167. package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
  168. package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
  169. package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
  170. package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
  171. package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
  172. package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
  173. package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
  174. package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
  175. package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
  176. package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
  177. package/lib/components/shared/three/ui/MenuList.js.map +1 -1
  178. package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
  179. package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
  180. package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
  181. package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
  182. package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
  183. package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
  184. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
  185. package/lib/components/shared/ui/BackButton.js.map +1 -1
  186. package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
  187. package/lib/components/shared/ui/CombatTimer.js.map +1 -1
  188. package/lib/components/shared/ui/ErrorModal.js.map +1 -1
  189. package/lib/components/shared/ui/LoadingState.js.map +1 -1
  190. package/lib/components/shared/ui/SplashScreen.js +2 -2
  191. package/lib/components/shared/ui/SplashScreen.js.map +1 -1
  192. package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
  193. package/lib/components/shared/ui/VolumeControl.js.map +1 -1
  194. package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
  195. package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
  196. package/lib/constants/bodyDimensions.js.map +1 -1
  197. package/lib/constants/bodyRenderingConstants.js.map +1 -1
  198. package/lib/data/archetypeClothing.js.map +1 -1
  199. package/lib/data/archetypePhysicalAttributes.js.map +1 -1
  200. package/lib/data/techniqueMappings.js.map +1 -1
  201. package/lib/data/techniques.js.map +1 -1
  202. package/lib/hooks/useActionFeedback.js.map +1 -1
  203. package/lib/hooks/useBalanceAnimations.js.map +1 -1
  204. package/lib/hooks/useCombatTimer.js.map +1 -1
  205. package/lib/hooks/useDebounce.js.map +1 -1
  206. package/lib/hooks/useHUDLayout.d.ts.map +1 -1
  207. package/lib/hooks/useHUDLayout.js +3 -2
  208. package/lib/hooks/useHUDLayout.js.map +1 -1
  209. package/lib/hooks/useHandPoseTransitions.js.map +1 -1
  210. package/lib/hooks/useKeyboardControls.js.map +1 -1
  211. package/lib/hooks/useMatchCountdown.js.map +1 -1
  212. package/lib/hooks/useMuscleActivation.js.map +1 -1
  213. package/lib/hooks/usePauseMenu.js.map +1 -1
  214. package/lib/hooks/usePlayerAnimation.js.map +1 -1
  215. package/lib/hooks/useResponsiveLayout.js.map +1 -1
  216. package/lib/hooks/useRoundTransition.js.map +1 -1
  217. package/lib/hooks/useSkeletalAnimation.d.ts.map +1 -1
  218. package/lib/hooks/useSkeletalAnimation.js +1 -1
  219. package/lib/hooks/useSkeletalAnimation.js.map +1 -1
  220. package/lib/hooks/useTechniqueSelection.js.map +1 -1
  221. package/lib/hooks/useThrottle.js.map +1 -1
  222. package/lib/hooks/useTouchControls.js.map +1 -1
  223. package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
  224. package/lib/hooks/useWindowSize.js.map +1 -1
  225. package/lib/systems/CombatSystem.js.map +1 -1
  226. package/lib/systems/EffectCalculator.js.map +1 -1
  227. package/lib/systems/LayoutSystem.js.map +1 -1
  228. package/lib/systems/PlayerEffectManager.js.map +1 -1
  229. package/lib/systems/ResponsiveScaling.js.map +1 -1
  230. package/lib/systems/TrigramSystem.js.map +1 -1
  231. package/lib/systems/VitalPointSystem.js.map +1 -1
  232. package/lib/systems/ai/AIPersonality.js.map +1 -1
  233. package/lib/systems/ai/AdaptiveDifficulty.js +16 -16
  234. package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
  235. package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
  236. package/lib/systems/ai/ComboSystem.js.map +1 -1
  237. package/lib/systems/ai/DecisionTree.js.map +1 -1
  238. package/lib/systems/ai/TrainingAI.js.map +1 -1
  239. package/lib/systems/ai/types.js.map +1 -1
  240. package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
  241. package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
  242. package/lib/systems/animation/builders/HandPoses.js.map +1 -1
  243. package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
  244. package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
  245. package/lib/systems/animation/builders/KickPhaseApplicator.d.ts +6 -0
  246. package/lib/systems/animation/builders/KickPhaseApplicator.d.ts.map +1 -1
  247. package/lib/systems/animation/builders/KickPhaseApplicator.js +16 -9
  248. package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
  249. package/lib/systems/animation/builders/KoreanGuardPositions.d.ts +4 -4
  250. package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
  251. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts +1 -1
  252. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts.map +1 -1
  253. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js +5 -5
  254. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
  255. package/lib/systems/animation/builders/MartialArtsConstants.d.ts +112 -71
  256. package/lib/systems/animation/builders/MartialArtsConstants.d.ts.map +1 -1
  257. package/lib/systems/animation/builders/MartialArtsConstants.js +113 -72
  258. package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
  259. package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
  260. package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
  261. package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
  262. package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
  263. package/lib/systems/animation/catalogs/AttackAnimations.js.map +1 -1
  264. package/lib/systems/animation/catalogs/BasicAnimations.js.map +1 -1
  265. package/lib/systems/animation/catalogs/ComboAnimations.js.map +1 -1
  266. package/lib/systems/animation/catalogs/DarkOpsAnimations.js.map +1 -1
  267. package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
  268. package/lib/systems/animation/catalogs/ElbowKneeAnimations.js.map +1 -1
  269. package/lib/systems/animation/catalogs/EnhancedAttackAnimations.js.map +1 -1
  270. package/lib/systems/animation/catalogs/EnhancedElbowKneeAnimations.js.map +1 -1
  271. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
  272. package/lib/systems/animation/catalogs/GamRedirectionAnimations.js.map +1 -1
  273. package/lib/systems/animation/catalogs/GamStanceAnimations.js +21 -0
  274. package/lib/systems/animation/catalogs/GamStanceAnimations.js.map +1 -0
  275. package/lib/systems/animation/catalogs/GamTechniqueAnimations.js +34 -2
  276. package/lib/systems/animation/catalogs/GamTechniqueAnimations.js.map +1 -1
  277. package/lib/systems/animation/catalogs/GanStanceAnimations.js.map +1 -1
  278. package/lib/systems/animation/catalogs/GanTechniqueAnimations.js.map +1 -1
  279. package/lib/systems/animation/catalogs/GeonStanceAnimations.js.map +1 -1
  280. package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts +9 -0
  281. package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts.map +1 -1
  282. package/lib/systems/animation/catalogs/GonTechniqueAnimations.js +288 -0
  283. package/lib/systems/animation/catalogs/GonTechniqueAnimations.js.map +1 -0
  284. package/lib/systems/animation/catalogs/GrapplingAnimations.js.map +1 -1
  285. package/lib/systems/animation/catalogs/JinStanceAnimations.js.map +1 -1
  286. package/lib/systems/animation/catalogs/JinTechniqueAnimations.js.map +1 -1
  287. package/lib/systems/animation/catalogs/KickAnimations.d.ts +2 -2
  288. package/lib/systems/animation/catalogs/KickAnimations.js +2 -2
  289. package/lib/systems/animation/catalogs/KickAnimations.js.map +1 -1
  290. package/lib/systems/animation/catalogs/LiStanceAnimations.js +14 -1
  291. package/lib/systems/animation/catalogs/LiStanceAnimations.js.map +1 -1
  292. package/lib/systems/animation/catalogs/LiTechniqueAnimations.js.map +1 -1
  293. package/lib/systems/animation/catalogs/MovementAnimations.js.map +1 -1
  294. package/lib/systems/animation/catalogs/PunchAnimations.d.ts +1 -1
  295. package/lib/systems/animation/catalogs/PunchAnimations.js +1 -1
  296. package/lib/systems/animation/catalogs/PunchAnimations.js.map +1 -1
  297. package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
  298. package/lib/systems/animation/catalogs/SonStanceAnimations.js.map +1 -1
  299. package/lib/systems/animation/catalogs/SonTechniqueAnimations.js.map +1 -1
  300. package/lib/systems/animation/catalogs/SpecializedPunchAnimations.js.map +1 -1
  301. package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
  302. package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
  303. package/lib/systems/animation/catalogs/StanceGuardPoses.d.ts +6 -6
  304. package/lib/systems/animation/catalogs/StanceGuardPoses.js +36 -36
  305. package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
  306. package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
  307. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
  308. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
  309. package/lib/systems/animation/catalogs/TaeJointLockAnimations.js.map +1 -1
  310. package/lib/systems/animation/catalogs/TaeStanceAnimations.js.map +1 -1
  311. package/lib/systems/animation/constants/AnatomicalLimits.js.map +1 -1
  312. package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
  313. package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
  314. package/lib/systems/animation/core/AnimationPriority.js +15 -15
  315. package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
  316. package/lib/systems/animation/core/AnimationRegistry.d.ts +30 -0
  317. package/lib/systems/animation/core/AnimationRegistry.d.ts.map +1 -1
  318. package/lib/systems/animation/core/AnimationRegistry.js +74 -12
  319. package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
  320. package/lib/systems/animation/core/AnimationStateMachine.js +16 -16
  321. package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
  322. package/lib/systems/animation/core/AnimationTransitions.d.ts.map +1 -1
  323. package/lib/systems/animation/core/AnimationTransitions.js +34 -0
  324. package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
  325. package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
  326. package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
  327. package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
  328. package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
  329. package/lib/systems/animation/core/index.d.ts +1 -1
  330. package/lib/systems/animation/core/index.d.ts.map +1 -1
  331. package/lib/systems/animation/core/types.d.ts +24 -0
  332. package/lib/systems/animation/core/types.d.ts.map +1 -1
  333. package/lib/systems/animation/core/types.js +27 -11
  334. package/lib/systems/animation/core/types.js.map +1 -1
  335. package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
  336. package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
  337. package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
  338. package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
  339. package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
  340. package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
  341. package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
  342. package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
  343. package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
  344. package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
  345. package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
  346. package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
  347. package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
  348. package/lib/systems/bodypart/types.js.map +1 -1
  349. package/lib/systems/breathing/BreathingDisruptionSystem.js +19 -19
  350. package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
  351. package/lib/systems/breathing/feedback.js.map +1 -1
  352. package/lib/systems/breathing/integration.js.map +1 -1
  353. package/lib/systems/combat/BalanceSystem.js +19 -19
  354. package/lib/systems/combat/BalanceSystem.js.map +1 -1
  355. package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
  356. package/lib/systems/combat/CombatStateSystem.js +17 -17
  357. package/lib/systems/combat/CombatStateSystem.js.map +1 -1
  358. package/lib/systems/combat/ConsciousnessSystem.js +24 -24
  359. package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
  360. package/lib/systems/combat/FallIntegration.js.map +1 -1
  361. package/lib/systems/combat/GrappleSystem.js.map +1 -1
  362. package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
  363. package/lib/systems/combat/PainResponseSystem.js +21 -21
  364. package/lib/systems/combat/PainResponseSystem.js.map +1 -1
  365. package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
  366. package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
  367. package/lib/systems/combat/typeGuards.js.map +1 -1
  368. package/lib/systems/effects.js.map +1 -1
  369. package/lib/systems/game.js.map +1 -1
  370. package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
  371. package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
  372. package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
  373. package/lib/systems/movement/integration.js.map +1 -1
  374. package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
  375. package/lib/systems/physics/CollisionDetection.js.map +1 -1
  376. package/lib/systems/physics/CoordinateMapper.js.map +1 -1
  377. package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
  378. package/lib/systems/physics/MovementPhysics.js.map +1 -1
  379. package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
  380. package/lib/systems/physics/SpeedModifierSystem.js +6 -6
  381. package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
  382. package/lib/systems/trigram/KoreanCulture.js.map +1 -1
  383. package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
  384. package/lib/systems/trigram/StanceManager.js.map +1 -1
  385. package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
  386. package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
  387. package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
  388. package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
  389. package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
  390. package/lib/systems/trigram/techniques/GeonTechniques.js.map +1 -1
  391. package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
  392. package/lib/systems/trigram/techniques/JinTechniques.js.map +1 -1
  393. package/lib/systems/trigram/techniques/LiTechniques.js.map +1 -1
  394. package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
  395. package/lib/systems/trigram/techniques/TaeTechniques.js.map +1 -1
  396. package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
  397. package/lib/systems/trigram/techniques/index.js.map +1 -1
  398. package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
  399. package/lib/systems/trigram/types.js.map +1 -1
  400. package/lib/systems/types.js.map +1 -1
  401. package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
  402. package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
  403. package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
  404. package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
  405. package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
  406. package/lib/systems/vitalpoint/VitalPointsData.js.map +1 -1
  407. package/lib/types/AccessibilityTypes.js.map +1 -1
  408. package/lib/types/LayoutTypes.js.map +1 -1
  409. package/lib/types/PhysicsTypes.js.map +1 -1
  410. package/lib/types/common.js.map +1 -1
  411. package/lib/types/constants/animations.js.map +1 -1
  412. package/lib/types/constants/colors.js.map +1 -1
  413. package/lib/types/constants/designSystem.js.map +1 -1
  414. package/lib/types/constants/index.js.map +1 -1
  415. package/lib/types/constants/layout.d.ts +21 -0
  416. package/lib/types/constants/layout.d.ts.map +1 -1
  417. package/lib/types/constants/layout.js +22 -1
  418. package/lib/types/constants/layout.js.map +1 -1
  419. package/lib/types/constants/performance.js.map +1 -1
  420. package/lib/types/constants/typography.js.map +1 -1
  421. package/lib/types/constants/ui.js.map +1 -1
  422. package/lib/types/facial.js +19 -19
  423. package/lib/types/facial.js.map +1 -1
  424. package/lib/types/hand-animation.js.map +1 -1
  425. package/lib/types/injury.js.map +1 -1
  426. package/lib/types/muscle.js.map +1 -1
  427. package/lib/types/physics.js.map +1 -1
  428. package/lib/types/physicsConstants.js.map +1 -1
  429. package/lib/types/player-visual.d.ts +1 -1
  430. package/lib/types/player-visual.d.ts.map +1 -1
  431. package/lib/types/skeletal.js.map +1 -1
  432. package/lib/types/techniqueId.js.map +1 -1
  433. package/lib/utils/accessibility.js.map +1 -1
  434. package/lib/utils/arenaWorldDimensions.js.map +1 -1
  435. package/lib/utils/assetConfig.js.map +1 -1
  436. package/lib/utils/characterScaling.js.map +1 -1
  437. package/lib/utils/colorHelpers.js.map +1 -1
  438. package/lib/utils/colorUtils.js.map +1 -1
  439. package/lib/utils/combatReadiness.js.map +1 -1
  440. package/lib/utils/controlMapping.js.map +1 -1
  441. package/lib/utils/deviceDetection.js +6 -7
  442. package/lib/utils/deviceDetection.js.map +1 -1
  443. package/lib/utils/effectUtils.js.map +1 -1
  444. package/lib/utils/fabricTextures.js.map +1 -1
  445. package/lib/utils/hapticFeedback.js.map +1 -1
  446. package/lib/utils/haptics.js.map +1 -1
  447. package/lib/utils/htmlOverlayHelpers.js.map +1 -1
  448. package/lib/utils/inputSystem.js.map +1 -1
  449. package/lib/utils/koreanThemeHelpers.js.map +1 -1
  450. package/lib/utils/math.js.map +1 -1
  451. package/lib/utils/mobileLayoutHelpers.js.map +1 -1
  452. package/lib/utils/mobileUIUtils.js.map +1 -1
  453. package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
  454. package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
  455. package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
  456. package/lib/utils/performanceOptimization.js.map +1 -1
  457. package/lib/utils/player3DHelpers.js.map +1 -1
  458. package/lib/utils/playerUtils.js.map +1 -1
  459. package/lib/utils/responsiveLayout.js.map +1 -1
  460. package/lib/utils/responsiveLayoutHelpers.d.ts +7 -0
  461. package/lib/utils/responsiveLayoutHelpers.d.ts.map +1 -1
  462. package/lib/utils/responsiveLayoutHelpers.js +16 -2
  463. package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
  464. package/lib/utils/responsiveOrientationConstants.js.map +1 -1
  465. package/lib/utils/safeAreaUtils.js.map +1 -1
  466. package/lib/utils/sharedPhysicsConfig.js.map +1 -1
  467. package/lib/utils/skeletonScaling.js.map +1 -1
  468. package/lib/utils/stanceHelpers.js.map +1 -1
  469. package/lib/utils/threeObjectPool.js.map +1 -1
  470. package/lib/utils/visualEffects.js.map +1 -1
  471. package/package.json +7 -7
@@ -17,6 +17,7 @@ import { physicalReachCalculator } from "../../../systems/physics/PhysicalReachC
17
17
  import { getArchetypePhysicalAttributes } from "../../../data/archetypePhysicalAttributes.js";
18
18
  import { getMobileControlsBottom } from "../../../types/constants/layout.js";
19
19
  import { usePlayerMovement } from "../../../utils/inputSystem.js";
20
+ import { getHUDPositionScale } from "../../../utils/responsiveLayoutHelpers.js";
20
21
  import { createCameraConfig } from "../../../utils/sharedPhysicsConfig.js";
21
22
  import { useKoreanTheme } from "../../shared/base/useKoreanTheme.js";
22
23
  import VitalPointMarkers3D from "../../shared/three/effects/VitalPointMarkers3D.js";
@@ -95,17 +96,7 @@ var TrainingScreen3D = ({ onPlayerUpdate, onReturnToMenu, width = 1200, height =
95
96
  size: "md",
96
97
  isMobile
97
98
  });
98
- const positionScale = React.useMemo(() => {
99
- if (isMobile) return 1;
100
- switch (screenSize) {
101
- case "mobile": return 1;
102
- case "tablet": return 1;
103
- case "desktop": return 1;
104
- case "large": return 1.25;
105
- case "xlarge": return 1.5;
106
- default: return 1;
107
- }
108
- }, [isMobile, screenSize]);
99
+ const positionScale = React.useMemo(() => getHUDPositionScale(screenSize, isMobile), [screenSize, isMobile]);
109
100
  const difficulty = "normal";
110
101
  const vitalPointCount = 70;
111
102
  const [selectedArchetype, setSelectedArchetype] = React.useState(initialArchetype);
@@ -385,6 +376,7 @@ var TrainingScreen3D = ({ onPlayerUpdate, onReturnToMenu, width = 1200, height =
385
376
  },
386
377
  playerAnimation: {
387
378
  transitionTo: playerAnimation.transitionTo,
379
+ transitionToAttack: playerAnimation.transitionToAttack,
388
380
  transitionToStanceGuard: playerAnimation.transitionToStanceGuard,
389
381
  currentState: playerAnimation.currentState
390
382
  },
@@ -1 +1 @@
1
- {"version":3,"file":"TrainingScreen3D.js","names":[],"sources":["../../../../src/components/screens/training/TrainingScreen3D.tsx"],"sourcesContent":["/**\n * TrainingScreen3D - Three.js-based training screen\n *\n * Refactored to use consolidated hooks matching CombatScreen architecture.\n * Provides 3D training dummy with vital point targeting and UI overlays.\n *\n * UI Rendering: All HUD elements are rendered in an absolute-positioned div\n * OUTSIDE the Canvas, matching CombatScreen's reliable rendering pattern.\n * This eliminates the need for Html overlays inside Three.js and ensures\n * HUDs appear immediately without waiting for Canvas initialization.\n *\n * Architecture (Consolidated in PR #1394 + Issue #1398):\n * - TrainingLeftHUD: Anatomy controls, guard indicator\n * - TrainingRightHUD: Training stats, mode selector, vital point selection\n * - TrainingTopHUD: Training controls, archetype selector, return button\n * - TrainingBottomHUD: Technique bar, feedback messages, mobile controls\n * - VitalPointOverlayControlsPure: Vital point overlay controls (pure DOM)\n *\n * All UI components render as pure DOM in the HUD overlay div (lines 1230+).\n * NO Html components from @react-three/drei are used inside the Canvas.\n * This ensures clean separation of 3D rendering and UI layers.\n *\n * @korean 훈련화면3D - 훈련 상태 훅을 사용한 리팩토링된 3D 훈련 화면\n */\n\nimport { Canvas, useFrame } from \"@react-three/fiber\";\nimport { AccelerationUpdater } from \"../../../systems/movement/helpers/AccelerationUpdater\";\nimport {\n isRunningSpeed,\n STEP_DISTANCE_THRESHOLDS,\n} from \"../../../systems/movement/helpers/accelerationUtils\";\nimport {\n Bloom,\n EffectComposer,\n Noise,\n Vignette,\n} from \"@react-three/postprocessing\";\nimport * as THREE from \"three\";\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useAudio } from \"../../../audio/AudioProvider\";\nimport { useCombatAudio } from \"../../screens/combat/hooks/useCombatAudio\";\nimport { getArchetypePhysicalAttributes } from \"../../../data/archetypePhysicalAttributes\";\nimport { usePlayerAnimation } from \"../../../hooks/usePlayerAnimation\";\nimport { useTechniqueSelection } from \"../../../hooks/useTechniqueSelection\";\nimport { GestureEvent } from \"../../../hooks/useTouchControls\";\nimport { useWebGLContextLossHandler } from \"../../../hooks/useWebGLContextLossHandler\";\nimport { PlayerState } from \"../../../systems\";\nimport {\n AnimationEvents,\n AnimationState,\n AnimationType,\n resolveTechniqueAnimation,\n} from \"../../../systems/animation\";\nimport { getAnimationForTechniqueOrDefault } from \"../../../systems/animation/core/TechniqueAnimationMapping\";\nimport { physicalReachCalculator } from \"../../../systems/physics\";\nimport {\n MovementType,\n SpeedModifierSystem,\n} from \"../../../systems/physics/SpeedModifierSystem\";\nimport { TRIGRAM_STANCES_ORDER } from \"../../../systems/trigram/types\";\nimport {\n CombatState,\n PlayerArchetype,\n Position,\n Technique,\n TrigramStance,\n} from \"../../../types\";\nimport { getPerformanceSettings } from \"../../../types/constants\";\nimport { getMobileControlsBottom } from \"../../../types/constants/layout\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { DEFAULT_BODY_RADIUS_METERS } from \"../../../types/physicsConstants\";\nimport { usePlayerMovement } from \"../../../utils/inputSystem\";\nimport { calculateDistance3D } from \"../../../utils/math\";\nimport { createCameraConfig } from \"../../../utils/sharedPhysicsConfig\";\nimport {\n animationStateToPlayerAnimation,\n convertPlayerStateToProps,\n} from \"../../../utils/player3DHelpers\";\nimport { useKoreanTheme } from \"../../shared/base/useKoreanTheme\";\nimport {\n GestureRecognizerPure,\n StanceWheelPure,\n} from \"../../shared/mobile\";\nimport {\n MobileControlsOverlay,\n type ButtonEventType,\n type Direction,\n type DPadEventType,\n} from \"../../shared/mobile/MobileControlsPure\";\nimport {\n Player3DWithTransitions,\n VitalPointMarkers3D,\n type BodyRegionFilter,\n} from \"../../shared/three\";\nimport { StanceChangeIndicator } from \"../../shared/three/indicators/StanceChangeIndicator\";\nimport { CombatArena3D } from \"../../shared/three/scene/CombatArena3D\";\nimport { VitalPointOverlayControlsPure } from \"../../shared/ui/VitalPointOverlayControlsPure\";\nimport AnatomyOverlay3D, {\n type AnatomyLayer,\n} from \"./components/AnatomyOverlay3D\";\nimport FootPlacementMarkers3D from \"./components/FootPlacementMarkers3D\";\nimport HitFeedbackEffect3D from \"./components/HitFeedbackEffect3D\";\nimport type { DifficultyMode } from \"./components/TrainingDummy3D\";\nimport TrainingDummy3D from \"./components/TrainingDummy3D\";\nimport {\n TrainingBottomHUD,\n TrainingLeftHUD,\n TrainingRightHUD,\n TrainingTopHUD,\n} from \"./components/hud\";\nimport { useAttackMovement } from \"./hooks/useAttackMovement\";\nimport useTrainingActions from \"./hooks/useTrainingActions\";\nimport { useTrainingLayout } from \"./hooks/useTrainingLayout\";\nimport useTrainingState from \"./hooks/useTrainingState\";\n\n/**\n * AnimationUpdater - Component that updates player animation at 60fps\n *\n * @korean 훈련애니메이션업데이터 - 60fps로 플레이어 애니메이션을 업데이트하는 컴포넌트\n */\ninterface TrainingAnimationUpdaterProps {\n readonly playerAnimation: ReturnType<typeof usePlayerAnimation>;\n}\n\nconst TrainingAnimationUpdater: React.FC<TrainingAnimationUpdaterProps> = ({\n playerAnimation,\n}) => {\n useFrame((_state, delta) => {\n playerAnimation.update(delta);\n });\n\n return null;\n};\n\n/**\n * Props for the TrainingScreen3D component\n */\nexport interface TrainingScreen3DProps {\n /** Callback to update player state */\n readonly onPlayerUpdate: (updates: Partial<PlayerState>) => void;\n /** Callback when returning to menu */\n readonly onReturnToMenu: () => void;\n /** Canvas width in pixels. Defaults to 1200 */\n readonly width?: number;\n /** Canvas height in pixels. Defaults to 800 */\n readonly height?: number;\n /** Initial archetype from IntroScreen selection. Defaults to MUSA */\n readonly initialArchetype?: PlayerArchetype;\n}\n\n/**\n * TrainingScreen3D Component\n * Three.js-based training screen with 3D dummy and Html UI\n *\n * Uses consolidated hooks for state management matching CombatScreen architecture.\n */\nexport const TrainingScreen3D: React.FC<TrainingScreen3DProps> = ({\n onPlayerUpdate,\n onReturnToMenu,\n width = 1200,\n height = 800,\n initialArchetype = PlayerArchetype.MUSA,\n}) => {\n\n\n const { state: trainingState, actions: trainingActions } = useTrainingState();\n\n const audio = useAudio();\n \n const { playBoneImpactSound, playAttackSound, playStanceChangeSound } =\n useCombatAudio();\n\n const { trainingAreaBounds, isMobile, isPortrait, screenSize } =\n useTrainingLayout(width, height);\n\n const theme = useKoreanTheme({\n variant: \"primary\",\n size: \"md\",\n isMobile,\n });\n\n const positionScale = React.useMemo(() => {\n if (isMobile) {\n return 1.0;\n }\n\n switch (screenSize) {\n case \"mobile\":\n return 1.0; // Mobile already has special handling\n case \"tablet\":\n return 1.0;\n case \"desktop\":\n return 1.0;\n case \"large\":\n return 1.25;\n case \"xlarge\":\n return 1.5; // 4K displays need 1.5x offsets\n default:\n return 1.0;\n }\n }, [isMobile, screenSize]);\n\n const difficulty: DifficultyMode = \"normal\";\n const vitalPointCount = 70; // Show all 70 vital points\n\n const [selectedArchetype, setSelectedArchetype] =\n React.useState<PlayerArchetype>(initialArchetype);\n\n const [overlayVisible, setOverlayVisible] = React.useState(false);\n const [severityFilters, setSeverityFilters] = React.useState<\n import(\"../../../types/common\").VitalPointSeverity[]\n >([]);\n const [regionFilter, setRegionFilter] =\n React.useState<BodyRegionFilter>(\"all\");\n const [searchQuery, setSearchQuery] = React.useState(\"\");\n const [showLabels, setShowLabels] = React.useState(true);\n const [animated, setAnimated] = React.useState(true);\n const [scale, setScale] = React.useState(1.2);\n\n\n const [attackAnimation, setAttackAnimation] = React.useState<\n string | undefined\n >(undefined);\n\n React.useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"v\" || e.key === \"V\") {\n setOverlayVisible((prev) => !prev);\n audio.playSFX(\"menu_select\");\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n window.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [audio]);\n\n\n const contextLossCountRef = useRef(0);\n\n useWebGLContextLossHandler({\n onContextLost: () => {\n console.warn(\"⚠️ WebGL context lost in TrainingScreen\");\n contextLossCountRef.current += 1;\n },\n onContextRestored: () => {\n console.log(\"✓ WebGL context restored in TrainingScreen\");\n },\n autoRestore: true,\n });\n\n\n const speedModifierSystem = useMemo(() => new SpeedModifierSystem(), []);\n\n const [speedModifiers, setSpeedModifiers] = useState({\n finalSpeed: 6.0, // BASE_WALK_SPEED (6.0 m/s for responsive combat)\n baseSpeed: 6.0,\n finalAcceleration: 12.0, // BASE_ACCELERATION (12.0 m/s² for quick response)\n });\n\n const [walkRunSpeeds, setWalkRunSpeeds] = useState({\n walkSpeed: 6.0,\n runSpeed: 10.0,\n });\n\n\n const initialPositionMeters = useMemo<Position>(\n () => ({\n x: trainingAreaBounds.worldWidthMeters * 0.0, // Centered laterally\n y: 0, // Centered vertically\n }),\n [trainingAreaBounds],\n );\n\n const handlePositionChange = useCallback(\n (newPosition: Position) => {\n onPlayerUpdate({ position: newPosition });\n },\n [onPlayerUpdate],\n );\n\n const movementBounds = useMemo(\n () => ({\n worldWidthMeters: trainingAreaBounds.worldWidthMeters,\n worldDepthMeters: trainingAreaBounds.worldDepthMeters,\n }),\n [trainingAreaBounds.worldWidthMeters, trainingAreaBounds.worldDepthMeters],\n );\n\n \n const movementTimeRef = useRef(0);\n const lastDirectionRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });\n \n const [accelerationBasedSpeed, setAccelerationBasedSpeed] = useState(\n walkRunSpeeds.walkSpeed\n );\n \n const isRunning = isRunningSpeed(accelerationBasedSpeed, walkRunSpeeds.runSpeed);\n\n const { playerPosition, isMoving, velocity } = usePlayerMovement({\n enabled: true, // Always allow movement in training screen\n bounds: movementBounds, // Use memoized bounds object\n onPositionChange: handlePositionChange, // Use memoized callback\n initialPositionMeters,\n currentStance: TRIGRAM_STANCES_ORDER[trainingState.currentStanceIndex],\n legInjuryFactor: 0, // No injury in training mode\n isRunning, // Use computed acceleration-based running state\n maxSpeedOverride: accelerationBasedSpeed,\n accelerationOverride: speedModifiers.finalAcceleration,\n });\n\n const player3DPosition = useMemo<[number, number, number]>(() => {\n return [playerPosition.x, 0, playerPosition.y];\n }, [playerPosition]);\n\n const dummyPosition = useMemo<[number, number, number]>(\n () => [trainingAreaBounds.worldWidthMeters * 0.15, 0, 0],\n [trainingAreaBounds.worldWidthMeters],\n );\n\n const centerToCenterDistance = useMemo(\n () => calculateDistance3D(player3DPosition, dummyPosition),\n [player3DPosition, dummyPosition],\n );\n\n const distanceToDummy = useMemo(\n () => Math.max(0, centerToCenterDistance - DEFAULT_BODY_RADIUS_METERS),\n [centerToCenterDistance],\n );\n\n const lastFacingRotationRef = useRef<number>(0);\n\n const playerRotation = useMemo(() => {\n if (isMoving && velocity && (velocity.x !== 0 || velocity.y !== 0)) {\n return Math.atan2(velocity.x, velocity.y);\n } else {\n const dx = dummyPosition[0] - player3DPosition[0];\n const dz = dummyPosition[2] - player3DPosition[2];\n return Math.atan2(dx, dz);\n }\n }, [isMoving, velocity, player3DPosition, dummyPosition]);\n\n useEffect(() => {\n lastFacingRotationRef.current = playerRotation;\n }, [playerRotation]);\n\n\n const [currentLaterality, setCurrentLaterality] = useState<\"left\" | \"right\">(\"right\");\n \n const stepCounterRef = useRef(0);\n const lastPositionRef = useRef<Position>(playerPosition);\n \n useEffect(() => {\n if (!isMoving) {\n stepCounterRef.current = 0;\n lastPositionRef.current = playerPosition;\n return;\n }\n\n const dx = playerPosition.x - lastPositionRef.current.x;\n const dy = playerPosition.y - lastPositionRef.current.y;\n const distanceMoved = Math.sqrt(dx * dx + dy * dy);\n \n const stepThreshold = isRunning \n ? STEP_DISTANCE_THRESHOLDS.RUN \n : STEP_DISTANCE_THRESHOLDS.WALK;\n stepCounterRef.current += distanceMoved;\n \n const stepsCrossed = Math.floor(stepCounterRef.current / stepThreshold);\n if (stepsCrossed > 0) {\n if (stepsCrossed % 2 === 1) {\n setCurrentLaterality(prev => prev === \"right\" ? \"left\" : \"right\");\n }\n stepCounterRef.current -= stepsCrossed * stepThreshold;\n }\n \n lastPositionRef.current = playerPosition;\n }, [playerPosition, isMoving, isRunning]);\n\n\n const pendingAttackRef = useRef<{\n accuracy: number;\n vitalPoint: string;\n animationType?: AnimationType;\n startTime?: number;\n techniqueId?: string;\n } | null>(null);\n\n const handleDummyHitRef = useRef<\n (\n vitalPointId: string,\n attackContext?: {\n animationType?: AnimationType;\n techniqueId?: string;\n },\n ) => boolean\n >(() => false);\n\n const playerAnimationRef = useRef<ReturnType<\n typeof usePlayerAnimation\n > | null>(null);\n\n const playerAnimationEvents = useMemo<AnimationEvents>(\n () => ({\n onFrame: (frame, state) => {\n if (state === \"attack\" && frame === 6 && pendingAttackRef.current) {\n const attackData = pendingAttackRef.current;\n handleDummyHitRef.current(attackData.vitalPoint, {\n animationType: attackData.animationType,\n techniqueId: attackData.techniqueId,\n });\n pendingAttackRef.current = null;\n }\n },\n onAnimationComplete: (state) => {\n if (state === \"stance_change\") {\n playStanceChangeSound();\n const currentStance =\n TRIGRAM_STANCES_ORDER[trainingState.currentStanceIndex];\n if (currentStance && playerAnimationRef.current) {\n playerAnimationRef.current.transitionToStanceGuard(currentStance);\n }\n }\n },\n }),\n [playStanceChangeSound, trainingState.currentStanceIndex],\n );\n\n const playerAnimation = usePlayerAnimation({\n events: playerAnimationEvents,\n });\n\n useEffect(() => {\n playerAnimationRef.current = playerAnimation;\n }, [playerAnimation]);\n\n\n const currentStance = useMemo(\n () => TRIGRAM_STANCES_ORDER[trainingState.currentStanceIndex],\n [trainingState.currentStanceIndex],\n );\n\n const [previousStanceIndex, setPreviousStanceIndex] = useState<number>(0);\n\n const currentTechniqueAnimationTypeRef = useRef<AnimationType>(\n AnimationType.JAB,\n );\n\n\n const trainingPlayerState = useMemo<PlayerState>(() => {\n return {\n id: \"training-player\",\n name: { korean: \"훈련생\", english: \"Trainee\" },\n archetype: selectedArchetype,\n health: 100,\n maxHealth: 100,\n ki: 100,\n maxKi: 100,\n stamina: 100,\n maxStamina: 100,\n energy: 100,\n maxEnergy: 100,\n attackPower: 10,\n defense: 10,\n speed: 10,\n technique: 10,\n pain: 0,\n consciousness: 100,\n balance: 100,\n momentum: 0,\n currentStance: TRIGRAM_STANCES_ORDER[trainingState.currentStanceIndex],\n combatState: CombatState.IDLE,\n position: playerPosition,\n isBlocking: false,\n isStunned: false,\n isCountering: false,\n lastActionTime: 0,\n recoveryTime: 0,\n lastStanceChangeTime: 0,\n statusEffects: [],\n activeEffects: [],\n vitalPoints: [],\n totalDamageReceived: 0,\n totalDamageDealt: 0,\n hitsTaken: 0,\n hitsLanded: trainingState.stats.hits,\n perfectStrikes: trainingState.perfectStrikes,\n vitalPointHits: 0,\n misses: trainingState.stats.misses,\n accuracy: trainingState.stats.accuracy,\n comboCount: trainingState.stats.combo,\n };\n }, [playerPosition, trainingState, selectedArchetype]);\n\n useEffect(() => {\n const updateSpeedModifiers = () => {\n const walkModifiers = speedModifierSystem.calculateSpeedModifiers(\n trainingPlayerState,\n MovementType.WALKING,\n false, // isCrouching\n );\n\n const runModifiers = speedModifierSystem.calculateSpeedModifiers(\n trainingPlayerState,\n MovementType.RUNNING,\n false, // isCrouching\n );\n\n setSpeedModifiers({\n finalSpeed: walkModifiers.finalSpeed,\n baseSpeed: walkModifiers.baseSpeed,\n finalAcceleration: walkModifiers.finalAcceleration,\n });\n\n setWalkRunSpeeds({\n walkSpeed: walkModifiers.finalSpeed,\n runSpeed: runModifiers.finalSpeed,\n });\n };\n\n updateSpeedModifiers();\n\n const intervalId = setInterval(updateSpeedModifiers, 200);\n\n return () => clearInterval(intervalId);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [trainingPlayerState]); // speedModifierSystem is memoized and never changes\n\n\n const isPlayerAttacking = useMemo(\n () => playerAnimation?.currentState === \"attack\",\n [playerAnimation],\n );\n\n const attackDirection = useMemo(() => {\n if (!isPlayerAttacking) {\n return new THREE.Vector3(0, 0, 1); // Default forward direction\n }\n const dx = dummyPosition[0] - player3DPosition[0];\n const dz = dummyPosition[2] - player3DPosition[2];\n return new THREE.Vector3(dx, 0, dz).normalize();\n }, [dummyPosition, player3DPosition, isPlayerAttacking]);\n\n const {\n currentPosition: player3DPositionWithAttackMovement,\n } = useAttackMovement({\n isAttacking: isPlayerAttacking,\n // eslint-disable-next-line react-hooks/refs -- ref value is set synchronously before isAttacking becomes true; hook only reads this at attack start\n animationType: currentTechniqueAnimationTypeRef.current,\n currentStance: trainingPlayerState.currentStance,\n basePosition: player3DPosition,\n attackDirection,\n animationDuration: 0.4,\n });\n\n const finalPlayer3DPosition = isPlayerAttacking\n ? player3DPositionWithAttackMovement\n : player3DPosition;\n\n\n const handleAttackRef = useRef<(() => void) | null>(null);\n\n const techniqueSelection = useTechniqueSelection({\n player: trainingPlayerState,\n enabled: trainingState.isTraining,\n onTechniqueExecute: useCallback(\n (technique: Technique) => {\n trainingActions.setFeedback(\n `${technique.name.korean} 사용! | Used ${technique.name.english}!`,\n );\n\n const animationName = resolveTechniqueAnimation(technique);\n setAttackAnimation(animationName);\n\n\n handleAttackRef.current?.();\n },\n [trainingActions],\n ),\n });\n\n const selectedTechniqueId = useMemo(() => {\n const techniques = techniqueSelection.availableTechniques;\n const selectedIdx = techniqueSelection.selectedIndex;\n if (techniques.length === 0 || selectedIdx < 0 || selectedIdx >= techniques.length) {\n return undefined;\n }\n return techniques[selectedIdx]?.id;\n }, [techniqueSelection.availableTechniques, techniqueSelection.selectedIndex]);\n\n const {\n handleStartTraining,\n handleStopTraining,\n handleDummyHit,\n handleDummyDefeated,\n handleStanceChange,\n handleAttack,\n } = useTrainingActions({\n state: trainingState,\n actions: trainingActions,\n playerPosition,\n player3DPosition,\n dummyPosition,\n playerArchetype: selectedArchetype,\n playerStance: currentStance,\n currentTechniqueAnimationTypeRef, // Ref for technique's animation type\n audio,\n playBoneImpactSound, // Pass bone impact audio function from useCombatAudio\n playAttackSound, // Pass attack sound function from useCombatAudio\n selectedTechniqueId, // Pass selected technique ID for intensity-based attack sounds\n onPlayerUpdate: (updates) => {\n onPlayerUpdate(updates);\n },\n playerAnimation: {\n transitionTo: playerAnimation.transitionTo,\n transitionToStanceGuard: playerAnimation.transitionToStanceGuard,\n currentState: playerAnimation.currentState,\n },\n pendingAttackRef, // Share the ref with animation events\n });\n\n useEffect(() => {\n handleAttackRef.current = handleAttack;\n }, [handleAttack]);\n\n useEffect(() => {\n handleDummyHitRef.current = handleDummyHit;\n }, [handleDummyHit]);\n\n const handleStanceChangeWithVisualFeedback = useCallback(\n (stanceIndex: number) => {\n setPreviousStanceIndex(trainingState.currentStanceIndex);\n handleStanceChange(stanceIndex);\n },\n [handleStanceChange, trainingState.currentStanceIndex],\n );\n\n\n const prevIsMovingRef = useRef<boolean>(isMoving);\n const prevIsRunningRef = useRef<boolean>(isRunning);\n const prevStanceRef = useRef<TrigramStance>(currentStance);\n \n useEffect(() => {\n const isMovingChanged = prevIsMovingRef.current !== isMoving;\n const isRunningChanged = prevIsRunningRef.current !== isRunning;\n const stanceChanged = prevStanceRef.current !== currentStance;\n \n if (isMovingChanged || isRunningChanged) {\n if (isMoving) {\n if (isRunning) {\n playerAnimation.transitionTo(AnimationState.RUN);\n } else {\n playerAnimation.transitionTo(AnimationState.WALK);\n }\n } else if (playerAnimation.currentState === AnimationState.WALK || \n playerAnimation.currentState === AnimationState.RUN) {\n playerAnimation.transitionToStanceGuard(currentStance);\n }\n prevIsMovingRef.current = isMoving;\n prevIsRunningRef.current = isRunning;\n }\n \n if (stanceChanged && !isMoving) {\n if (playerAnimation.currentState === AnimationState.IDLE || \n playerAnimation.isInStanceGuard()) {\n playerAnimation.transitionToStanceGuard(currentStance);\n }\n prevStanceRef.current = currentStance;\n }\n }, [isMoving, isRunning, currentStance, playerAnimation]);\n\n\n const cooldownsMap = useMemo(() => {\n const map = new Map<string, number>();\n techniqueSelection.activeCooldowns.forEach((cd) => {\n map.set(cd.techniqueId, cd.remaining);\n });\n return map;\n }, [techniqueSelection.activeCooldowns]);\n\n const { currentTechniqueReach, currentAnimationType } = useMemo(() => {\n const techniques = techniqueSelection.availableTechniques;\n const selectedIdx = techniqueSelection.selectedIndex;\n if (techniques.length === 0) {\n return {\n currentTechniqueReach: 0.7,\n currentAnimationType: AnimationType.JAB,\n };\n }\n const currentTechnique =\n techniques[Math.min(selectedIdx, techniques.length - 1)];\n if (!currentTechnique) {\n return {\n currentTechniqueReach: 0.7,\n currentAnimationType: AnimationType.JAB,\n };\n }\n const animConfig = getAnimationForTechniqueOrDefault(currentTechnique.id);\n const physicalAttributes =\n getArchetypePhysicalAttributes(selectedArchetype);\n const reach = physicalReachCalculator.calculateMaxReach(\n physicalAttributes,\n animConfig.type,\n currentStance,\n );\n return {\n currentTechniqueReach: reach,\n currentAnimationType: animConfig.type,\n };\n }, [\n techniqueSelection.availableTechniques,\n techniqueSelection.selectedIndex,\n selectedArchetype,\n currentStance,\n ]);\n\n useEffect(() => {\n currentTechniqueAnimationTypeRef.current = currentAnimationType;\n }, [currentAnimationType]);\n\n\n const activeMobileKeyRef = useRef<string | null>(null);\n\n const mobileControlsEnabled = isMobile;\n\n const handleMobileMove = useCallback(\n (direction: Direction | null, eventType: DPadEventType) => {\n const directionMap: Record<Direction, string> = {\n up: \"w\",\n \"up-right\": \"w\",\n right: \"d\",\n \"down-right\": \"s\",\n down: \"s\",\n \"down-left\": \"s\",\n left: \"a\",\n \"up-left\": \"w\",\n };\n\n if (eventType === \"start\" && direction) {\n if (\n activeMobileKeyRef.current &&\n activeMobileKeyRef.current !== directionMap[direction]\n ) {\n const prevKey = activeMobileKeyRef.current;\n window.dispatchEvent(\n new KeyboardEvent(\"keyup\", {\n key: prevKey,\n code: `Key${prevKey.toUpperCase()}`,\n bubbles: true,\n cancelable: true,\n }),\n );\n }\n\n const key = directionMap[direction];\n activeMobileKeyRef.current = key;\n window.dispatchEvent(\n new KeyboardEvent(\"keydown\", {\n key,\n code: `Key${key.toUpperCase()}`,\n bubbles: true,\n cancelable: true,\n }),\n );\n } else if (eventType === \"end\") {\n if (activeMobileKeyRef.current) {\n const key = activeMobileKeyRef.current;\n window.dispatchEvent(\n new KeyboardEvent(\"keyup\", {\n key,\n code: `Key${key.toUpperCase()}`,\n bubbles: true,\n cancelable: true,\n }),\n );\n activeMobileKeyRef.current = null;\n }\n }\n },\n [],\n );\n\n const handleMobileAttack = useCallback(() => {\n handleAttack();\n }, [handleAttack]);\n\n const handleMobileBlock = useCallback(\n (eventType: ButtonEventType) => {\n if (eventType === \"start\") {\n audio.playSFX(\"block\");\n }\n },\n [audio],\n );\n\n const handleMobileGesture = useCallback(\n (gesture: GestureEvent) => {\n switch (gesture.type) {\n case \"swipe-right\":\n window.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"d\" }));\n break;\n case \"swipe-left\":\n window.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"a\" }));\n break;\n case \"swipe-up\":\n if (trainingState.isTraining) {\n window.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \" \" }));\n }\n break;\n case \"swipe-down\":\n trainingActions.resetDummy();\n break;\n case \"two-finger-tap\":\n trainingActions.setTrainingMode(\n trainingState.trainingMode === \"vital_point\"\n ? \"basics\"\n : \"vital_point\",\n );\n audio.playSFX(\"menu_select\");\n break;\n }\n },\n [trainingState, trainingActions, audio],\n );\n\n const handleMobileStanceChange = useCallback(\n (stanceIndex: number) => {\n handleStanceChangeWithVisualFeedback(stanceIndex);\n },\n [handleStanceChangeWithVisualFeedback],\n );\n\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n const key = event.key.toLowerCase();\n\n if (key === \"escape\") {\n onReturnToMenu();\n return;\n }\n\n if (key >= \"1\" && key <= \"8\") {\n const stanceIndex = parseInt(key) - 1;\n handleStanceChangeWithVisualFeedback(stanceIndex);\n event.preventDefault();\n return;\n }\n\n if (key === \" \") {\n handleAttack();\n event.preventDefault();\n return;\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [onReturnToMenu, handleStanceChangeWithVisualFeedback, handleAttack]);\n\n\n const hasMountedRef = useRef(false);\n\n useEffect(() => {\n let audioStarted = false;\n\n const startMusic = async () => {\n try {\n await audio.fadeIn(\"cyberpunk_fusion\", 2000);\n audioStarted = true;\n } catch (err) {\n console.warn(\"Failed to start training music:\", err);\n trainingActions.setFeedback(\n \"오디오 초기화 실패 | Audio initialization failed\",\n );\n }\n };\n\n void startMusic();\n\n return () => {\n if (audioStarted) {\n void audio\n .fadeOut(2000)\n .then(() => audio.stopMusic())\n .catch((err) => console.warn(\"Failed to stop training music:\", err));\n }\n };\n }, [audio, trainingActions]);\n\n useEffect(() => {\n if (!hasMountedRef.current) {\n hasMountedRef.current = true;\n handleStartTraining();\n }\n }, [handleStartTraining]);\n\n\n useEffect(() => {\n if (trainingState.showFeedback) {\n const timer = setTimeout(() => trainingActions.hideFeedback(), 1500);\n return () => clearTimeout(timer);\n }\n }, [trainingState.showFeedback, trainingState.feedback, trainingActions]);\n\n useEffect(() => {\n if (!trainingState.isTraining || !trainingState.sessionStartTime) return;\n\n const interval = setInterval(() => {\n trainingActions.updateSessionDuration(\n Math.floor((Date.now() - (trainingState.sessionStartTime ?? 0)) / 1000),\n );\n }, 1000);\n\n return () => clearInterval(interval);\n }, [\n trainingState.isTraining,\n trainingState.sessionStartTime,\n trainingActions,\n ]);\n\n const prevTrainingModeRef = useRef<typeof trainingState.trainingMode>(\n trainingState.trainingMode,\n );\n const isFirstModeEffectRef = useRef<boolean>(true);\n const isTrainingRef = useRef<boolean>(trainingState.isTraining);\n const modeChangeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n isTrainingRef.current = trainingState.isTraining;\n }, [trainingState.isTraining]);\n\n const handleStartTrainingRef = useRef(handleStartTraining);\n const handleStopTrainingRef = useRef(handleStopTraining);\n\n useEffect(() => {\n handleStartTrainingRef.current = handleStartTraining;\n handleStopTrainingRef.current = handleStopTraining;\n }, [handleStartTraining, handleStopTraining]);\n\n useEffect(() => {\n if (isFirstModeEffectRef.current) {\n isFirstModeEffectRef.current = false;\n prevTrainingModeRef.current = trainingState.trainingMode;\n return;\n }\n\n const previousMode = prevTrainingModeRef.current;\n const modeChanged = previousMode !== trainingState.trainingMode;\n\n if (!modeChanged) {\n return;\n }\n\n prevTrainingModeRef.current = trainingState.trainingMode;\n\n if (modeChangeTimerRef.current) {\n clearTimeout(modeChangeTimerRef.current);\n modeChangeTimerRef.current = null;\n }\n\n if (isTrainingRef.current) {\n handleStopTrainingRef.current();\n }\n\n modeChangeTimerRef.current = setTimeout(() => {\n handleStartTrainingRef.current();\n modeChangeTimerRef.current = null;\n }, 100);\n\n return () => {\n if (modeChangeTimerRef.current) {\n clearTimeout(modeChangeTimerRef.current);\n modeChangeTimerRef.current = null;\n }\n };\n }, [trainingState.trainingMode]); // Only depend on training mode to avoid unnecessary re-runs\n\n\n const handleEffectComplete = useCallback(\n (effectId: number) => {\n trainingActions.removeHitEffect(effectId);\n },\n [trainingActions],\n );\n\n\n const handleAnatomyLayerToggle = useCallback(\n (layer: AnatomyLayer) => {\n trainingActions.toggleAnatomyLayer(layer);\n audio.playSFX(\"menu_click\");\n },\n [trainingActions, audio],\n );\n\n const handleVitalPointClick = useCallback(\n (pointId: string) => {\n trainingActions.setSelectedVitalPoint(pointId);\n audio.playSFX(\"menu_select\");\n },\n [trainingActions, audio],\n );\n\n\n const cameraConfig = useMemo(() => {\n const base = createCameraConfig(isMobile);\n if (!isPortrait) return base;\n return {\n ...base,\n fov: Math.min(80, base.fov + 15),\n position: [base.position[0], base.position[1], base.position[2] + 4] as [\n number,\n number,\n number,\n ],\n };\n }, [isMobile, isPortrait]);\n\n\n const performanceSettings = useMemo(() => {\n return getPerformanceSettings(width, isMobile);\n }, [width, isMobile]);\n\n\n return (\n <div\n style={{\n width: `${width}px`,\n height: `${height}px`,\n position: \"relative\",\n overflow: \"hidden\", // Prevent content from extending beyond container\n }}\n data-testid=\"training-screen-3d\"\n >\n <Canvas\n style={{ width: `${width}px`, height: `${height}px` }}\n gl={{\n antialias: performanceSettings.antialias,\n alpha: false,\n powerPreference: \"high-performance\",\n failIfMajorPerformanceCaveat: false, // Don't fail in software renderer\n preserveDrawingBuffer: true, // Help with context stability\n }}\n dpr={performanceSettings.dpr}\n shadows={false} // Temporarily disable shadows\n onCreated={({ gl }) => {\n gl.setClearColor(theme.colors.UI_BACKGROUND_DARK, 1);\n }}\n camera={cameraConfig}\n >\n {/* Lighting - base lighting, arena provides additional */}\n <ambientLight intensity={0.6} />\n <directionalLight position={[10, 10, 5]} intensity={1.2} />\n\n {/* Combat Arena 3D Environment - uses physics-based world dimensions */}\n <CombatArena3D\n lighting=\"cyberpunk\"\n scale={trainingAreaBounds.scale}\n worldWidthMeters={trainingAreaBounds.worldWidthMeters}\n worldDepthMeters={trainingAreaBounds.worldDepthMeters}\n />\n\n {/* Animation updater - 60fps updates */}\n <TrainingAnimationUpdater playerAnimation={playerAnimation} />\n\n {/* Acceleration updater - tracks movement time and updates speed */}\n <AccelerationUpdater\n isMoving={isMoving}\n velocity={velocity}\n movementTimeRef={movementTimeRef}\n lastDirectionRef={lastDirectionRef}\n onSpeedUpdate={setAccelerationBasedSpeed}\n walkSpeed={walkRunSpeeds.walkSpeed}\n runSpeed={walkRunSpeeds.runSpeed}\n />\n\n {/* Training dummy at fixed position */}\n <TrainingDummy3D\n position={dummyPosition}\n selectedVitalPoint={trainingState.selectedVitalPoint}\n isTraining={trainingState.isTraining}\n health={trainingState.dummyHealth}\n onVitalPointHit={handleDummyHit}\n onDefeated={handleDummyDefeated}\n difficulty={difficulty}\n vitalPointCount={vitalPointCount}\n isMobile={isMobile}\n />\n\n {/* Anatomy overlay for educational visualization */}\n {trainingState.visibleAnatomyLayers.length > 0 && (\n <AnatomyOverlay3D\n position={dummyPosition}\n visibleLayers={trainingState.visibleAnatomyLayers}\n opacity={0.6}\n isMobile={isMobile}\n />\n )}\n\n {/* Vital Point Overlay - Show all 70 points on dummy */}\n {overlayVisible && (\n <VitalPointMarkers3D\n position={dummyPosition}\n visible={overlayVisible}\n severityFilter={severityFilters}\n regionFilter={regionFilter}\n searchQuery={searchQuery}\n showLabels={showLabels}\n scale={scale}\n animated={animated}\n selectedPoint={trainingState.selectedVitalPoint}\n onPointClick={handleVitalPointClick}\n />\n )}\n\n {/* Player model */}\n <Player3DWithTransitions\n {...convertPlayerStateToProps(\n trainingPlayerState,\n finalPlayer3DPosition,\n playerRotation,\n {\n isMobile,\n facing: \"right\",\n enableFacialExpressions: true,\n enableEyeTracking: true,\n opponentPosition: dummyPosition,\n },\n )}\n currentAnimation={animationStateToPlayerAnimation(\n playerAnimation.currentState,\n )}\n attackAnimation={attackAnimation}\n laterality={currentLaterality}\n enableTransitionEffects={!isMobile}\n enableStanceSymbol={true}\n enableStanceAudio={true}\n />\n\n {/* Foot Placement Markers for Footwork Drills */}\n {trainingState.trainingMode === \"footwork\" &&\n trainingState.footworkDrillActive && (\n <FootPlacementMarkers3D\n centerPosition={dummyPosition}\n pattern={\n trainingState.footworkDrillType === \"free_practice\"\n ? \"none\"\n : trainingState.footworkDrillType\n }\n currentStep={trainingState.footworkDrillStep}\n visible={true}\n scale={1.0}\n animated={true}\n />\n )}\n\n {/* Hit effects */}\n {trainingState.hitEffects.map((effect) => (\n <HitFeedbackEffect3D\n key={effect.id}\n position={effect.position}\n type={effect.type}\n damage={effect.damage}\n visible={effect.visible}\n onComplete={() => handleEffectComplete(effect.id)}\n isMobile={isMobile}\n />\n ))}\n\n {/* Stance Change Visual Indicator */}\n <StanceChangeIndicator\n currentStance={trainingState.currentStanceIndex}\n previousStance={previousStanceIndex}\n isMobile={isMobile}\n />\n\n {/* NOTE: Mobile controls moved OUTSIDE Canvas for reliable touch events */}\n {/* See MobileControlsPure component rendered after HUDs */}\n\n {/* Post-processing Effects - desktop high tier only for Android WebGL stability */}\n {performanceSettings.postProcessing && (\n <EffectComposer multisampling={4}>\n <Bloom\n luminanceThreshold={0.9}\n luminanceSmoothing={0.9}\n mipmapBlur\n intensity={0.8}\n radius={0.4}\n />\n <Noise opacity={0.03} />\n <Vignette eskil={false} offset={0.1} darkness={0.3} />\n </EffectComposer>\n )}\n </Canvas>\n\n {/* Html UI Overlays (positioned absolutely over Canvas) - matches CombatScreen pattern */}\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: Z_INDEX.HUD,\n overflow: \"clip\",\n }}\n data-testid=\"training-hud-overlay\"\n >\n {/* Left HUD - Anatomy Controls, Guard Indicator.\n Hidden on mobile because the side HUD occludes the compressed\n arena in both portrait and landscape. Anatomy layer toggles remain\n available on larger viewports where there is room for side panels. */}\n {!isMobile && (\n <TrainingLeftHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n visibleAnatomyLayers={trainingState.visibleAnatomyLayers}\n onAnatomyLayerToggle={handleAnatomyLayerToggle}\n currentStanceIndex={trainingState.currentStanceIndex}\n isInGuard={playerAnimation.isInStanceGuard()}\n />\n )}\n\n {/* Top HUD - Training Controls, Archetype Selector, Return Button. */}\n <TrainingTopHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n isTraining={trainingState.isTraining}\n onStartTraining={handleStartTraining}\n onStopTraining={handleStopTraining}\n selectedArchetype={selectedArchetype}\n onArchetypeSelect={setSelectedArchetype}\n overlayVisible={overlayVisible}\n onReturnToMenu={onReturnToMenu}\n onPlaySFX={(sound) => audio.playSFX(sound)}\n />\n\n {/* Right HUD - Mode Selector, Stats, Vital Point Selection.\n Hidden on mobile to keep the training dojang visible and usable.\n The core start/stop, archetype, vital-point toggle, technique bar,\n stance wheel, gestures, and touch controls remain available. */}\n {!isMobile && (\n <TrainingRightHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n trainingMode={trainingState.trainingMode}\n onModeChange={trainingActions.setTrainingMode}\n stats={{\n ...trainingState.stats,\n sessionDuration: trainingState.sessionDuration,\n bestCombo: trainingState.bestCombo,\n perfectStrikes: trainingState.perfectStrikes,\n }}\n distanceToDummy={distanceToDummy}\n effectiveReach={currentTechniqueReach}\n selectedVitalPoint={trainingState.selectedVitalPoint}\n onVitalPointSelect={trainingActions.setSelectedVitalPoint}\n footworkDrillType={trainingState.footworkDrillType}\n footworkDrillStep={trainingState.footworkDrillStep}\n footworkDrillActive={trainingState.footworkDrillActive}\n onStartFootworkDrill={trainingActions.startFootworkDrill}\n onStopFootworkDrill={trainingActions.stopFootworkDrill}\n onAdvanceFootworkStep={trainingActions.advanceFootworkStep}\n />\n )}\n {/* Bottom HUD - Technique Bar, Feedback Messages, Mobile Controls */}\n <TrainingBottomHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n techniques={techniqueSelection.availableTechniques}\n player={trainingPlayerState}\n selectedIndex={techniqueSelection.selectedIndex}\n cooldowns={cooldownsMap}\n onTechniqueSelect={techniqueSelection.selectTechnique}\n showFeedback={trainingState.showFeedback}\n feedbackMessage={trainingState.feedback}\n selectedArchetype={selectedArchetype}\n onArchetypeSelect={setSelectedArchetype}\n onPlaySFX={(sound) => audio.playSFX(sound)}\n />\n\n {/* Vital Point Overlay Controls - Pure DOM overlay (outside Canvas) */}\n {overlayVisible && (\n <VitalPointOverlayControlsPure\n visible={overlayVisible}\n onVisibleChange={setOverlayVisible}\n severityFilters={severityFilters}\n onSeverityFiltersChange={setSeverityFilters}\n regionFilter={regionFilter}\n onRegionFilterChange={setRegionFilter}\n searchQuery={searchQuery}\n onSearchQueryChange={setSearchQuery}\n showLabels={showLabels}\n onShowLabelsChange={setShowLabels}\n animated={animated}\n onAnimatedChange={setAnimated}\n scale={scale}\n onScaleChange={setScale}\n screenPosition={{ top: \"180px\", left: \"20px\" }}\n isMobile={isMobile}\n />\n )}\n\n {/* Mobile Controls - Pure DOM overlay (outside Canvas for reliable touch) */}\n {isMobile && (\n <>\n <MobileControlsOverlay\n onMove={handleMobileMove}\n onAttack={handleMobileAttack}\n onBlock={handleMobileBlock}\n disabled={!mobileControlsEnabled}\n bottom={getMobileControlsBottom(height)}\n opacity={0.85}\n viewportWidth={width}\n viewportHeight={height}\n />\n\n <StanceWheelPure\n currentStance={trainingState.currentStanceIndex}\n onStanceChange={handleMobileStanceChange}\n expanded={trainingState.stanceWheelExpanded}\n onToggle={trainingActions.toggleStanceWheel}\n disabled={!mobileControlsEnabled}\n opacity={0.8}\n />\n\n <GestureRecognizerPure\n onGesture={handleMobileGesture}\n enabled={mobileControlsEnabled}\n showFeedback={true}\n minSwipeDistance={50}\n />\n </>\n )}\n </div>\n </div>\n );\n};\n\nexport default TrainingScreen3D;\n"],"mappings":"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"}
1
+ {"version":3,"file":"TrainingScreen3D.js","names":[],"sources":["../../../../src/components/screens/training/TrainingScreen3D.tsx"],"sourcesContent":["/**\n * TrainingScreen3D - Three.js-based training screen\n *\n * Refactored to use consolidated hooks matching CombatScreen architecture.\n * Provides 3D training dummy with vital point targeting and UI overlays.\n *\n * UI Rendering: All HUD elements are rendered in an absolute-positioned div\n * OUTSIDE the Canvas, matching CombatScreen's reliable rendering pattern.\n * This eliminates the need for Html overlays inside Three.js and ensures\n * HUDs appear immediately without waiting for Canvas initialization.\n *\n * Architecture (Consolidated in PR #1394 + Issue #1398):\n * - TrainingLeftHUD: Anatomy controls, guard indicator\n * - TrainingRightHUD: Training stats, mode selector, vital point selection\n * - TrainingTopHUD: Training controls, archetype selector, return button\n * - TrainingBottomHUD: Technique bar, feedback messages, mobile controls\n * - VitalPointOverlayControlsPure: Vital point overlay controls (pure DOM)\n *\n * All UI components render as pure DOM in the HUD overlay div (lines 1230+).\n * NO Html components from @react-three/drei are used inside the Canvas.\n * This ensures clean separation of 3D rendering and UI layers.\n *\n * @korean 훈련화면3D - 훈련 상태 훅을 사용한 리팩토링된 3D 훈련 화면\n */\n\nimport { Canvas, useFrame } from \"@react-three/fiber\";\nimport { AccelerationUpdater } from \"../../../systems/movement/helpers/AccelerationUpdater\";\nimport {\n isRunningSpeed,\n STEP_DISTANCE_THRESHOLDS,\n} from \"../../../systems/movement/helpers/accelerationUtils\";\nimport {\n Bloom,\n EffectComposer,\n Noise,\n Vignette,\n} from \"@react-three/postprocessing\";\nimport * as THREE from \"three\";\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useAudio } from \"../../../audio/AudioProvider\";\nimport { useCombatAudio } from \"../../screens/combat/hooks/useCombatAudio\";\nimport { getArchetypePhysicalAttributes } from \"../../../data/archetypePhysicalAttributes\";\nimport { usePlayerAnimation } from \"../../../hooks/usePlayerAnimation\";\nimport { useTechniqueSelection } from \"../../../hooks/useTechniqueSelection\";\nimport { GestureEvent } from \"../../../hooks/useTouchControls\";\nimport { useWebGLContextLossHandler } from \"../../../hooks/useWebGLContextLossHandler\";\nimport { PlayerState } from \"../../../systems\";\nimport {\n AnimationEvents,\n AnimationState,\n AnimationType,\n resolveTechniqueAnimation,\n} from \"../../../systems/animation\";\nimport { getAnimationForTechniqueOrDefault } from \"../../../systems/animation/core/TechniqueAnimationMapping\";\nimport { physicalReachCalculator } from \"../../../systems/physics\";\nimport {\n MovementType,\n SpeedModifierSystem,\n} from \"../../../systems/physics/SpeedModifierSystem\";\nimport { TRIGRAM_STANCES_ORDER } from \"../../../systems/trigram/types\";\nimport {\n CombatState,\n PlayerArchetype,\n Position,\n Technique,\n TrigramStance,\n} from \"../../../types\";\nimport { getPerformanceSettings } from \"../../../types/constants\";\nimport { getMobileControlsBottom } from \"../../../types/constants/layout\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { DEFAULT_BODY_RADIUS_METERS } from \"../../../types/physicsConstants\";\nimport { usePlayerMovement } from \"../../../utils/inputSystem\";\nimport { calculateDistance3D } from \"../../../utils/math\";\nimport { getHUDPositionScale } from \"../../../utils/responsiveLayoutHelpers\";\nimport { createCameraConfig } from \"../../../utils/sharedPhysicsConfig\";\nimport {\n animationStateToPlayerAnimation,\n convertPlayerStateToProps,\n} from \"../../../utils/player3DHelpers\";\nimport { useKoreanTheme } from \"../../shared/base/useKoreanTheme\";\nimport {\n GestureRecognizerPure,\n StanceWheelPure,\n} from \"../../shared/mobile\";\nimport {\n MobileControlsOverlay,\n type ButtonEventType,\n type Direction,\n type DPadEventType,\n} from \"../../shared/mobile/MobileControlsPure\";\nimport {\n Player3DWithTransitions,\n VitalPointMarkers3D,\n type BodyRegionFilter,\n} from \"../../shared/three\";\nimport { StanceChangeIndicator } from \"../../shared/three/indicators/StanceChangeIndicator\";\nimport { CombatArena3D } from \"../../shared/three/scene/CombatArena3D\";\nimport { VitalPointOverlayControlsPure } from \"../../shared/ui/VitalPointOverlayControlsPure\";\nimport AnatomyOverlay3D, {\n type AnatomyLayer,\n} from \"./components/AnatomyOverlay3D\";\nimport FootPlacementMarkers3D from \"./components/FootPlacementMarkers3D\";\nimport HitFeedbackEffect3D from \"./components/HitFeedbackEffect3D\";\nimport type { DifficultyMode } from \"./components/TrainingDummy3D\";\nimport TrainingDummy3D from \"./components/TrainingDummy3D\";\nimport {\n TrainingBottomHUD,\n TrainingLeftHUD,\n TrainingRightHUD,\n TrainingTopHUD,\n} from \"./components/hud\";\nimport { useAttackMovement } from \"./hooks/useAttackMovement\";\nimport useTrainingActions from \"./hooks/useTrainingActions\";\nimport { useTrainingLayout } from \"./hooks/useTrainingLayout\";\nimport useTrainingState from \"./hooks/useTrainingState\";\n\n/**\n * AnimationUpdater - Component that updates player animation at 60fps\n *\n * @korean 훈련애니메이션업데이터 - 60fps로 플레이어 애니메이션을 업데이트하는 컴포넌트\n */\ninterface TrainingAnimationUpdaterProps {\n readonly playerAnimation: ReturnType<typeof usePlayerAnimation>;\n}\n\nconst TrainingAnimationUpdater: React.FC<TrainingAnimationUpdaterProps> = ({\n playerAnimation,\n}) => {\n useFrame((_state, delta) => {\n playerAnimation.update(delta);\n });\n\n return null;\n};\n\n/**\n * Props for the TrainingScreen3D component\n */\nexport interface TrainingScreen3DProps {\n /** Callback to update player state */\n readonly onPlayerUpdate: (updates: Partial<PlayerState>) => void;\n /** Callback when returning to menu */\n readonly onReturnToMenu: () => void;\n /** Canvas width in pixels. Defaults to 1200 */\n readonly width?: number;\n /** Canvas height in pixels. Defaults to 800 */\n readonly height?: number;\n /** Initial archetype from IntroScreen selection. Defaults to MUSA */\n readonly initialArchetype?: PlayerArchetype;\n}\n\n/**\n * TrainingScreen3D Component\n * Three.js-based training screen with 3D dummy and Html UI\n *\n * Uses consolidated hooks for state management matching CombatScreen architecture.\n */\nexport const TrainingScreen3D: React.FC<TrainingScreen3DProps> = ({\n onPlayerUpdate,\n onReturnToMenu,\n width = 1200,\n height = 800,\n initialArchetype = PlayerArchetype.MUSA,\n}) => {\n\n\n const { state: trainingState, actions: trainingActions } = useTrainingState();\n\n const audio = useAudio();\n \n const { playBoneImpactSound, playAttackSound, playStanceChangeSound } =\n useCombatAudio();\n\n const { trainingAreaBounds, isMobile, isPortrait, screenSize } =\n useTrainingLayout(width, height);\n\n const theme = useKoreanTheme({\n variant: \"primary\",\n size: \"md\",\n isMobile,\n });\n\n const positionScale = React.useMemo(\n () => getHUDPositionScale(screenSize, isMobile),\n [screenSize, isMobile],\n );\n\n const difficulty: DifficultyMode = \"normal\";\n const vitalPointCount = 70; // Show all 70 vital points\n\n const [selectedArchetype, setSelectedArchetype] =\n React.useState<PlayerArchetype>(initialArchetype);\n\n const [overlayVisible, setOverlayVisible] = React.useState(false);\n const [severityFilters, setSeverityFilters] = React.useState<\n import(\"../../../types/common\").VitalPointSeverity[]\n >([]);\n const [regionFilter, setRegionFilter] =\n React.useState<BodyRegionFilter>(\"all\");\n const [searchQuery, setSearchQuery] = React.useState(\"\");\n const [showLabels, setShowLabels] = React.useState(true);\n const [animated, setAnimated] = React.useState(true);\n const [scale, setScale] = React.useState(1.2);\n\n\n const [attackAnimation, setAttackAnimation] = React.useState<\n string | undefined\n >(undefined);\n\n React.useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"v\" || e.key === \"V\") {\n setOverlayVisible((prev) => !prev);\n audio.playSFX(\"menu_select\");\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n window.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [audio]);\n\n\n const contextLossCountRef = useRef(0);\n\n useWebGLContextLossHandler({\n onContextLost: () => {\n console.warn(\"⚠️ WebGL context lost in TrainingScreen\");\n contextLossCountRef.current += 1;\n },\n onContextRestored: () => {\n console.log(\"✓ WebGL context restored in TrainingScreen\");\n },\n autoRestore: true,\n });\n\n\n const speedModifierSystem = useMemo(() => new SpeedModifierSystem(), []);\n\n const [speedModifiers, setSpeedModifiers] = useState({\n finalSpeed: 6.0, // BASE_WALK_SPEED (6.0 m/s for responsive combat)\n baseSpeed: 6.0,\n finalAcceleration: 12.0, // BASE_ACCELERATION (12.0 m/s² for quick response)\n });\n\n const [walkRunSpeeds, setWalkRunSpeeds] = useState({\n walkSpeed: 6.0,\n runSpeed: 10.0,\n });\n\n\n const initialPositionMeters = useMemo<Position>(\n () => ({\n x: trainingAreaBounds.worldWidthMeters * 0.0, // Centered laterally\n y: 0, // Centered vertically\n }),\n [trainingAreaBounds],\n );\n\n const handlePositionChange = useCallback(\n (newPosition: Position) => {\n onPlayerUpdate({ position: newPosition });\n },\n [onPlayerUpdate],\n );\n\n const movementBounds = useMemo(\n () => ({\n worldWidthMeters: trainingAreaBounds.worldWidthMeters,\n worldDepthMeters: trainingAreaBounds.worldDepthMeters,\n }),\n [trainingAreaBounds.worldWidthMeters, trainingAreaBounds.worldDepthMeters],\n );\n\n \n const movementTimeRef = useRef(0);\n const lastDirectionRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });\n \n const [accelerationBasedSpeed, setAccelerationBasedSpeed] = useState(\n walkRunSpeeds.walkSpeed\n );\n \n const isRunning = isRunningSpeed(accelerationBasedSpeed, walkRunSpeeds.runSpeed);\n\n const { playerPosition, isMoving, velocity } = usePlayerMovement({\n enabled: true, // Always allow movement in training screen\n bounds: movementBounds, // Use memoized bounds object\n onPositionChange: handlePositionChange, // Use memoized callback\n initialPositionMeters,\n currentStance: TRIGRAM_STANCES_ORDER[trainingState.currentStanceIndex],\n legInjuryFactor: 0, // No injury in training mode\n isRunning, // Use computed acceleration-based running state\n maxSpeedOverride: accelerationBasedSpeed,\n accelerationOverride: speedModifiers.finalAcceleration,\n });\n\n const player3DPosition = useMemo<[number, number, number]>(() => {\n return [playerPosition.x, 0, playerPosition.y];\n }, [playerPosition]);\n\n const dummyPosition = useMemo<[number, number, number]>(\n () => [trainingAreaBounds.worldWidthMeters * 0.15, 0, 0],\n [trainingAreaBounds.worldWidthMeters],\n );\n\n const centerToCenterDistance = useMemo(\n () => calculateDistance3D(player3DPosition, dummyPosition),\n [player3DPosition, dummyPosition],\n );\n\n const distanceToDummy = useMemo(\n () => Math.max(0, centerToCenterDistance - DEFAULT_BODY_RADIUS_METERS),\n [centerToCenterDistance],\n );\n\n const lastFacingRotationRef = useRef<number>(0);\n\n const playerRotation = useMemo(() => {\n if (isMoving && velocity && (velocity.x !== 0 || velocity.y !== 0)) {\n return Math.atan2(velocity.x, velocity.y);\n } else {\n const dx = dummyPosition[0] - player3DPosition[0];\n const dz = dummyPosition[2] - player3DPosition[2];\n return Math.atan2(dx, dz);\n }\n }, [isMoving, velocity, player3DPosition, dummyPosition]);\n\n useEffect(() => {\n lastFacingRotationRef.current = playerRotation;\n }, [playerRotation]);\n\n\n const [currentLaterality, setCurrentLaterality] = useState<\"left\" | \"right\">(\"right\");\n \n const stepCounterRef = useRef(0);\n const lastPositionRef = useRef<Position>(playerPosition);\n \n useEffect(() => {\n if (!isMoving) {\n stepCounterRef.current = 0;\n lastPositionRef.current = playerPosition;\n return;\n }\n\n const dx = playerPosition.x - lastPositionRef.current.x;\n const dy = playerPosition.y - lastPositionRef.current.y;\n const distanceMoved = Math.sqrt(dx * dx + dy * dy);\n \n const stepThreshold = isRunning \n ? STEP_DISTANCE_THRESHOLDS.RUN \n : STEP_DISTANCE_THRESHOLDS.WALK;\n stepCounterRef.current += distanceMoved;\n \n const stepsCrossed = Math.floor(stepCounterRef.current / stepThreshold);\n if (stepsCrossed > 0) {\n if (stepsCrossed % 2 === 1) {\n setCurrentLaterality(prev => prev === \"right\" ? \"left\" : \"right\");\n }\n stepCounterRef.current -= stepsCrossed * stepThreshold;\n }\n \n lastPositionRef.current = playerPosition;\n }, [playerPosition, isMoving, isRunning]);\n\n\n const pendingAttackRef = useRef<{\n accuracy: number;\n vitalPoint: string;\n animationType?: AnimationType;\n startTime?: number;\n techniqueId?: string;\n } | null>(null);\n\n const handleDummyHitRef = useRef<\n (\n vitalPointId: string,\n attackContext?: {\n animationType?: AnimationType;\n techniqueId?: string;\n },\n ) => boolean\n >(() => false);\n\n const playerAnimationRef = useRef<ReturnType<\n typeof usePlayerAnimation\n > | null>(null);\n\n const playerAnimationEvents = useMemo<AnimationEvents>(\n () => ({\n onFrame: (frame, state) => {\n if (state === \"attack\" && frame === 6 && pendingAttackRef.current) {\n const attackData = pendingAttackRef.current;\n handleDummyHitRef.current(attackData.vitalPoint, {\n animationType: attackData.animationType,\n techniqueId: attackData.techniqueId,\n });\n pendingAttackRef.current = null;\n }\n },\n onAnimationComplete: (state) => {\n if (state === \"stance_change\") {\n playStanceChangeSound();\n const currentStance =\n TRIGRAM_STANCES_ORDER[trainingState.currentStanceIndex];\n if (currentStance && playerAnimationRef.current) {\n playerAnimationRef.current.transitionToStanceGuard(currentStance);\n }\n }\n },\n }),\n [playStanceChangeSound, trainingState.currentStanceIndex],\n );\n\n const playerAnimation = usePlayerAnimation({\n events: playerAnimationEvents,\n });\n\n useEffect(() => {\n playerAnimationRef.current = playerAnimation;\n }, [playerAnimation]);\n\n\n const currentStance = useMemo(\n () => TRIGRAM_STANCES_ORDER[trainingState.currentStanceIndex],\n [trainingState.currentStanceIndex],\n );\n\n const [previousStanceIndex, setPreviousStanceIndex] = useState<number>(0);\n\n const currentTechniqueAnimationTypeRef = useRef<AnimationType>(\n AnimationType.JAB,\n );\n\n\n const trainingPlayerState = useMemo<PlayerState>(() => {\n return {\n id: \"training-player\",\n name: { korean: \"훈련생\", english: \"Trainee\" },\n archetype: selectedArchetype,\n health: 100,\n maxHealth: 100,\n ki: 100,\n maxKi: 100,\n stamina: 100,\n maxStamina: 100,\n energy: 100,\n maxEnergy: 100,\n attackPower: 10,\n defense: 10,\n speed: 10,\n technique: 10,\n pain: 0,\n consciousness: 100,\n balance: 100,\n momentum: 0,\n currentStance: TRIGRAM_STANCES_ORDER[trainingState.currentStanceIndex],\n combatState: CombatState.IDLE,\n position: playerPosition,\n isBlocking: false,\n isStunned: false,\n isCountering: false,\n lastActionTime: 0,\n recoveryTime: 0,\n lastStanceChangeTime: 0,\n statusEffects: [],\n activeEffects: [],\n vitalPoints: [],\n totalDamageReceived: 0,\n totalDamageDealt: 0,\n hitsTaken: 0,\n hitsLanded: trainingState.stats.hits,\n perfectStrikes: trainingState.perfectStrikes,\n vitalPointHits: 0,\n misses: trainingState.stats.misses,\n accuracy: trainingState.stats.accuracy,\n comboCount: trainingState.stats.combo,\n };\n }, [playerPosition, trainingState, selectedArchetype]);\n\n useEffect(() => {\n const updateSpeedModifiers = () => {\n const walkModifiers = speedModifierSystem.calculateSpeedModifiers(\n trainingPlayerState,\n MovementType.WALKING,\n false, // isCrouching\n );\n\n const runModifiers = speedModifierSystem.calculateSpeedModifiers(\n trainingPlayerState,\n MovementType.RUNNING,\n false, // isCrouching\n );\n\n setSpeedModifiers({\n finalSpeed: walkModifiers.finalSpeed,\n baseSpeed: walkModifiers.baseSpeed,\n finalAcceleration: walkModifiers.finalAcceleration,\n });\n\n setWalkRunSpeeds({\n walkSpeed: walkModifiers.finalSpeed,\n runSpeed: runModifiers.finalSpeed,\n });\n };\n\n updateSpeedModifiers();\n\n const intervalId = setInterval(updateSpeedModifiers, 200);\n\n return () => clearInterval(intervalId);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [trainingPlayerState]); // speedModifierSystem is memoized and never changes\n\n\n const isPlayerAttacking = useMemo(\n () => playerAnimation?.currentState === \"attack\",\n [playerAnimation],\n );\n\n const attackDirection = useMemo(() => {\n if (!isPlayerAttacking) {\n return new THREE.Vector3(0, 0, 1); // Default forward direction\n }\n const dx = dummyPosition[0] - player3DPosition[0];\n const dz = dummyPosition[2] - player3DPosition[2];\n return new THREE.Vector3(dx, 0, dz).normalize();\n }, [dummyPosition, player3DPosition, isPlayerAttacking]);\n\n const {\n currentPosition: player3DPositionWithAttackMovement,\n } = useAttackMovement({\n isAttacking: isPlayerAttacking,\n // eslint-disable-next-line react-hooks/refs -- ref value is set synchronously before isAttacking becomes true; hook only reads this at attack start\n animationType: currentTechniqueAnimationTypeRef.current,\n currentStance: trainingPlayerState.currentStance,\n basePosition: player3DPosition,\n attackDirection,\n animationDuration: 0.4,\n });\n\n const finalPlayer3DPosition = isPlayerAttacking\n ? player3DPositionWithAttackMovement\n : player3DPosition;\n\n\n const handleAttackRef = useRef<(() => void) | null>(null);\n\n const techniqueSelection = useTechniqueSelection({\n player: trainingPlayerState,\n enabled: trainingState.isTraining,\n onTechniqueExecute: useCallback(\n (technique: Technique) => {\n trainingActions.setFeedback(\n `${technique.name.korean} 사용! | Used ${technique.name.english}!`,\n );\n\n const animationName = resolveTechniqueAnimation(technique);\n setAttackAnimation(animationName);\n\n\n handleAttackRef.current?.();\n },\n [trainingActions],\n ),\n });\n\n const selectedTechniqueId = useMemo(() => {\n const techniques = techniqueSelection.availableTechniques;\n const selectedIdx = techniqueSelection.selectedIndex;\n if (techniques.length === 0 || selectedIdx < 0 || selectedIdx >= techniques.length) {\n return undefined;\n }\n return techniques[selectedIdx]?.id;\n }, [techniqueSelection.availableTechniques, techniqueSelection.selectedIndex]);\n\n const {\n handleStartTraining,\n handleStopTraining,\n handleDummyHit,\n handleDummyDefeated,\n handleStanceChange,\n handleAttack,\n } = useTrainingActions({\n state: trainingState,\n actions: trainingActions,\n playerPosition,\n player3DPosition,\n dummyPosition,\n playerArchetype: selectedArchetype,\n playerStance: currentStance,\n currentTechniqueAnimationTypeRef, // Ref for technique's animation type\n audio,\n playBoneImpactSound, // Pass bone impact audio function from useCombatAudio\n playAttackSound, // Pass attack sound function from useCombatAudio\n selectedTechniqueId, // Pass selected technique ID for intensity-based attack sounds\n onPlayerUpdate: (updates) => {\n onPlayerUpdate(updates);\n },\n playerAnimation: {\n transitionTo: playerAnimation.transitionTo,\n transitionToAttack: playerAnimation.transitionToAttack,\n transitionToStanceGuard: playerAnimation.transitionToStanceGuard,\n currentState: playerAnimation.currentState,\n },\n pendingAttackRef, // Share the ref with animation events\n });\n\n useEffect(() => {\n handleAttackRef.current = handleAttack;\n }, [handleAttack]);\n\n useEffect(() => {\n handleDummyHitRef.current = handleDummyHit;\n }, [handleDummyHit]);\n\n const handleStanceChangeWithVisualFeedback = useCallback(\n (stanceIndex: number) => {\n setPreviousStanceIndex(trainingState.currentStanceIndex);\n handleStanceChange(stanceIndex);\n },\n [handleStanceChange, trainingState.currentStanceIndex],\n );\n\n\n const prevIsMovingRef = useRef<boolean>(isMoving);\n const prevIsRunningRef = useRef<boolean>(isRunning);\n const prevStanceRef = useRef<TrigramStance>(currentStance);\n \n useEffect(() => {\n const isMovingChanged = prevIsMovingRef.current !== isMoving;\n const isRunningChanged = prevIsRunningRef.current !== isRunning;\n const stanceChanged = prevStanceRef.current !== currentStance;\n \n if (isMovingChanged || isRunningChanged) {\n if (isMoving) {\n if (isRunning) {\n playerAnimation.transitionTo(AnimationState.RUN);\n } else {\n playerAnimation.transitionTo(AnimationState.WALK);\n }\n } else if (playerAnimation.currentState === AnimationState.WALK || \n playerAnimation.currentState === AnimationState.RUN) {\n playerAnimation.transitionToStanceGuard(currentStance);\n }\n prevIsMovingRef.current = isMoving;\n prevIsRunningRef.current = isRunning;\n }\n \n if (stanceChanged && !isMoving) {\n if (playerAnimation.currentState === AnimationState.IDLE || \n playerAnimation.isInStanceGuard()) {\n playerAnimation.transitionToStanceGuard(currentStance);\n }\n prevStanceRef.current = currentStance;\n }\n }, [isMoving, isRunning, currentStance, playerAnimation]);\n\n\n const cooldownsMap = useMemo(() => {\n const map = new Map<string, number>();\n techniqueSelection.activeCooldowns.forEach((cd) => {\n map.set(cd.techniqueId, cd.remaining);\n });\n return map;\n }, [techniqueSelection.activeCooldowns]);\n\n const { currentTechniqueReach, currentAnimationType } = useMemo(() => {\n const techniques = techniqueSelection.availableTechniques;\n const selectedIdx = techniqueSelection.selectedIndex;\n if (techniques.length === 0) {\n return {\n currentTechniqueReach: 0.7,\n currentAnimationType: AnimationType.JAB,\n };\n }\n const currentTechnique =\n techniques[Math.min(selectedIdx, techniques.length - 1)];\n if (!currentTechnique) {\n return {\n currentTechniqueReach: 0.7,\n currentAnimationType: AnimationType.JAB,\n };\n }\n const animConfig = getAnimationForTechniqueOrDefault(currentTechnique.id);\n const physicalAttributes =\n getArchetypePhysicalAttributes(selectedArchetype);\n const reach = physicalReachCalculator.calculateMaxReach(\n physicalAttributes,\n animConfig.type,\n currentStance,\n );\n return {\n currentTechniqueReach: reach,\n currentAnimationType: animConfig.type,\n };\n }, [\n techniqueSelection.availableTechniques,\n techniqueSelection.selectedIndex,\n selectedArchetype,\n currentStance,\n ]);\n\n useEffect(() => {\n currentTechniqueAnimationTypeRef.current = currentAnimationType;\n }, [currentAnimationType]);\n\n\n const activeMobileKeyRef = useRef<string | null>(null);\n\n const mobileControlsEnabled = isMobile;\n\n const handleMobileMove = useCallback(\n (direction: Direction | null, eventType: DPadEventType) => {\n const directionMap: Record<Direction, string> = {\n up: \"w\",\n \"up-right\": \"w\",\n right: \"d\",\n \"down-right\": \"s\",\n down: \"s\",\n \"down-left\": \"s\",\n left: \"a\",\n \"up-left\": \"w\",\n };\n\n if (eventType === \"start\" && direction) {\n if (\n activeMobileKeyRef.current &&\n activeMobileKeyRef.current !== directionMap[direction]\n ) {\n const prevKey = activeMobileKeyRef.current;\n window.dispatchEvent(\n new KeyboardEvent(\"keyup\", {\n key: prevKey,\n code: `Key${prevKey.toUpperCase()}`,\n bubbles: true,\n cancelable: true,\n }),\n );\n }\n\n const key = directionMap[direction];\n activeMobileKeyRef.current = key;\n window.dispatchEvent(\n new KeyboardEvent(\"keydown\", {\n key,\n code: `Key${key.toUpperCase()}`,\n bubbles: true,\n cancelable: true,\n }),\n );\n } else if (eventType === \"end\") {\n if (activeMobileKeyRef.current) {\n const key = activeMobileKeyRef.current;\n window.dispatchEvent(\n new KeyboardEvent(\"keyup\", {\n key,\n code: `Key${key.toUpperCase()}`,\n bubbles: true,\n cancelable: true,\n }),\n );\n activeMobileKeyRef.current = null;\n }\n }\n },\n [],\n );\n\n const handleMobileAttack = useCallback(() => {\n handleAttack();\n }, [handleAttack]);\n\n const handleMobileBlock = useCallback(\n (eventType: ButtonEventType) => {\n if (eventType === \"start\") {\n audio.playSFX(\"block\");\n }\n },\n [audio],\n );\n\n const handleMobileGesture = useCallback(\n (gesture: GestureEvent) => {\n switch (gesture.type) {\n case \"swipe-right\":\n window.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"d\" }));\n break;\n case \"swipe-left\":\n window.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"a\" }));\n break;\n case \"swipe-up\":\n if (trainingState.isTraining) {\n window.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \" \" }));\n }\n break;\n case \"swipe-down\":\n trainingActions.resetDummy();\n break;\n case \"two-finger-tap\":\n trainingActions.setTrainingMode(\n trainingState.trainingMode === \"vital_point\"\n ? \"basics\"\n : \"vital_point\",\n );\n audio.playSFX(\"menu_select\");\n break;\n }\n },\n [trainingState, trainingActions, audio],\n );\n\n const handleMobileStanceChange = useCallback(\n (stanceIndex: number) => {\n handleStanceChangeWithVisualFeedback(stanceIndex);\n },\n [handleStanceChangeWithVisualFeedback],\n );\n\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n const key = event.key.toLowerCase();\n\n if (key === \"escape\") {\n onReturnToMenu();\n return;\n }\n\n if (key >= \"1\" && key <= \"8\") {\n const stanceIndex = parseInt(key) - 1;\n handleStanceChangeWithVisualFeedback(stanceIndex);\n event.preventDefault();\n return;\n }\n\n if (key === \" \") {\n handleAttack();\n event.preventDefault();\n return;\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [onReturnToMenu, handleStanceChangeWithVisualFeedback, handleAttack]);\n\n\n const hasMountedRef = useRef(false);\n\n useEffect(() => {\n let audioStarted = false;\n\n const startMusic = async () => {\n try {\n await audio.fadeIn(\"cyberpunk_fusion\", 2000);\n audioStarted = true;\n } catch (err) {\n console.warn(\"Failed to start training music:\", err);\n trainingActions.setFeedback(\n \"오디오 초기화 실패 | Audio initialization failed\",\n );\n }\n };\n\n void startMusic();\n\n return () => {\n if (audioStarted) {\n void audio\n .fadeOut(2000)\n .then(() => audio.stopMusic())\n .catch((err) => console.warn(\"Failed to stop training music:\", err));\n }\n };\n }, [audio, trainingActions]);\n\n useEffect(() => {\n if (!hasMountedRef.current) {\n hasMountedRef.current = true;\n handleStartTraining();\n }\n }, [handleStartTraining]);\n\n\n useEffect(() => {\n if (trainingState.showFeedback) {\n const timer = setTimeout(() => trainingActions.hideFeedback(), 1500);\n return () => clearTimeout(timer);\n }\n }, [trainingState.showFeedback, trainingState.feedback, trainingActions]);\n\n useEffect(() => {\n if (!trainingState.isTraining || !trainingState.sessionStartTime) return;\n\n const interval = setInterval(() => {\n trainingActions.updateSessionDuration(\n Math.floor((Date.now() - (trainingState.sessionStartTime ?? 0)) / 1000),\n );\n }, 1000);\n\n return () => clearInterval(interval);\n }, [\n trainingState.isTraining,\n trainingState.sessionStartTime,\n trainingActions,\n ]);\n\n const prevTrainingModeRef = useRef<typeof trainingState.trainingMode>(\n trainingState.trainingMode,\n );\n const isFirstModeEffectRef = useRef<boolean>(true);\n const isTrainingRef = useRef<boolean>(trainingState.isTraining);\n const modeChangeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n isTrainingRef.current = trainingState.isTraining;\n }, [trainingState.isTraining]);\n\n const handleStartTrainingRef = useRef(handleStartTraining);\n const handleStopTrainingRef = useRef(handleStopTraining);\n\n useEffect(() => {\n handleStartTrainingRef.current = handleStartTraining;\n handleStopTrainingRef.current = handleStopTraining;\n }, [handleStartTraining, handleStopTraining]);\n\n useEffect(() => {\n if (isFirstModeEffectRef.current) {\n isFirstModeEffectRef.current = false;\n prevTrainingModeRef.current = trainingState.trainingMode;\n return;\n }\n\n const previousMode = prevTrainingModeRef.current;\n const modeChanged = previousMode !== trainingState.trainingMode;\n\n if (!modeChanged) {\n return;\n }\n\n prevTrainingModeRef.current = trainingState.trainingMode;\n\n if (modeChangeTimerRef.current) {\n clearTimeout(modeChangeTimerRef.current);\n modeChangeTimerRef.current = null;\n }\n\n if (isTrainingRef.current) {\n handleStopTrainingRef.current();\n }\n\n modeChangeTimerRef.current = setTimeout(() => {\n handleStartTrainingRef.current();\n modeChangeTimerRef.current = null;\n }, 100);\n\n return () => {\n if (modeChangeTimerRef.current) {\n clearTimeout(modeChangeTimerRef.current);\n modeChangeTimerRef.current = null;\n }\n };\n }, [trainingState.trainingMode]); // Only depend on training mode to avoid unnecessary re-runs\n\n\n const handleEffectComplete = useCallback(\n (effectId: number) => {\n trainingActions.removeHitEffect(effectId);\n },\n [trainingActions],\n );\n\n\n const handleAnatomyLayerToggle = useCallback(\n (layer: AnatomyLayer) => {\n trainingActions.toggleAnatomyLayer(layer);\n audio.playSFX(\"menu_click\");\n },\n [trainingActions, audio],\n );\n\n const handleVitalPointClick = useCallback(\n (pointId: string) => {\n trainingActions.setSelectedVitalPoint(pointId);\n audio.playSFX(\"menu_select\");\n },\n [trainingActions, audio],\n );\n\n\n const cameraConfig = useMemo(() => {\n const base = createCameraConfig(isMobile);\n if (!isPortrait) return base;\n return {\n ...base,\n fov: Math.min(80, base.fov + 15),\n position: [base.position[0], base.position[1], base.position[2] + 4] as [\n number,\n number,\n number,\n ],\n };\n }, [isMobile, isPortrait]);\n\n\n const performanceSettings = useMemo(() => {\n return getPerformanceSettings(width, isMobile);\n }, [width, isMobile]);\n\n\n return (\n <div\n style={{\n width: `${width}px`,\n height: `${height}px`,\n position: \"relative\",\n overflow: \"hidden\", // Prevent content from extending beyond container\n }}\n data-testid=\"training-screen-3d\"\n >\n <Canvas\n style={{ width: `${width}px`, height: `${height}px` }}\n gl={{\n antialias: performanceSettings.antialias,\n alpha: false,\n powerPreference: \"high-performance\",\n failIfMajorPerformanceCaveat: false, // Don't fail in software renderer\n preserveDrawingBuffer: true, // Help with context stability\n }}\n dpr={performanceSettings.dpr}\n shadows={false} // Temporarily disable shadows\n onCreated={({ gl }) => {\n gl.setClearColor(theme.colors.UI_BACKGROUND_DARK, 1);\n }}\n camera={cameraConfig}\n >\n {/* Lighting - base lighting, arena provides additional */}\n <ambientLight intensity={0.6} />\n <directionalLight position={[10, 10, 5]} intensity={1.2} />\n\n {/* Combat Arena 3D Environment - uses physics-based world dimensions */}\n <CombatArena3D\n lighting=\"cyberpunk\"\n scale={trainingAreaBounds.scale}\n worldWidthMeters={trainingAreaBounds.worldWidthMeters}\n worldDepthMeters={trainingAreaBounds.worldDepthMeters}\n />\n\n {/* Animation updater - 60fps updates */}\n <TrainingAnimationUpdater playerAnimation={playerAnimation} />\n\n {/* Acceleration updater - tracks movement time and updates speed */}\n <AccelerationUpdater\n isMoving={isMoving}\n velocity={velocity}\n movementTimeRef={movementTimeRef}\n lastDirectionRef={lastDirectionRef}\n onSpeedUpdate={setAccelerationBasedSpeed}\n walkSpeed={walkRunSpeeds.walkSpeed}\n runSpeed={walkRunSpeeds.runSpeed}\n />\n\n {/* Training dummy at fixed position */}\n <TrainingDummy3D\n position={dummyPosition}\n selectedVitalPoint={trainingState.selectedVitalPoint}\n isTraining={trainingState.isTraining}\n health={trainingState.dummyHealth}\n onVitalPointHit={handleDummyHit}\n onDefeated={handleDummyDefeated}\n difficulty={difficulty}\n vitalPointCount={vitalPointCount}\n isMobile={isMobile}\n />\n\n {/* Anatomy overlay for educational visualization */}\n {trainingState.visibleAnatomyLayers.length > 0 && (\n <AnatomyOverlay3D\n position={dummyPosition}\n visibleLayers={trainingState.visibleAnatomyLayers}\n opacity={0.6}\n isMobile={isMobile}\n />\n )}\n\n {/* Vital Point Overlay - Show all 70 points on dummy */}\n {overlayVisible && (\n <VitalPointMarkers3D\n position={dummyPosition}\n visible={overlayVisible}\n severityFilter={severityFilters}\n regionFilter={regionFilter}\n searchQuery={searchQuery}\n showLabels={showLabels}\n scale={scale}\n animated={animated}\n selectedPoint={trainingState.selectedVitalPoint}\n onPointClick={handleVitalPointClick}\n />\n )}\n\n {/* Player model */}\n <Player3DWithTransitions\n {...convertPlayerStateToProps(\n trainingPlayerState,\n finalPlayer3DPosition,\n playerRotation,\n {\n isMobile,\n facing: \"right\",\n enableFacialExpressions: true,\n enableEyeTracking: true,\n opponentPosition: dummyPosition,\n },\n )}\n currentAnimation={animationStateToPlayerAnimation(\n playerAnimation.currentState,\n )}\n attackAnimation={attackAnimation}\n laterality={currentLaterality}\n enableTransitionEffects={!isMobile}\n enableStanceSymbol={true}\n enableStanceAudio={true}\n />\n\n {/* Foot Placement Markers for Footwork Drills */}\n {trainingState.trainingMode === \"footwork\" &&\n trainingState.footworkDrillActive && (\n <FootPlacementMarkers3D\n centerPosition={dummyPosition}\n pattern={\n trainingState.footworkDrillType === \"free_practice\"\n ? \"none\"\n : trainingState.footworkDrillType\n }\n currentStep={trainingState.footworkDrillStep}\n visible={true}\n scale={1.0}\n animated={true}\n />\n )}\n\n {/* Hit effects */}\n {trainingState.hitEffects.map((effect) => (\n <HitFeedbackEffect3D\n key={effect.id}\n position={effect.position}\n type={effect.type}\n damage={effect.damage}\n visible={effect.visible}\n onComplete={() => handleEffectComplete(effect.id)}\n isMobile={isMobile}\n />\n ))}\n\n {/* Stance Change Visual Indicator */}\n <StanceChangeIndicator\n currentStance={trainingState.currentStanceIndex}\n previousStance={previousStanceIndex}\n isMobile={isMobile}\n />\n\n {/* NOTE: Mobile controls moved OUTSIDE Canvas for reliable touch events */}\n {/* See MobileControlsPure component rendered after HUDs */}\n\n {/* Post-processing Effects - desktop high tier only for Android WebGL stability */}\n {performanceSettings.postProcessing && (\n <EffectComposer multisampling={4}>\n <Bloom\n luminanceThreshold={0.9}\n luminanceSmoothing={0.9}\n mipmapBlur\n intensity={0.8}\n radius={0.4}\n />\n <Noise opacity={0.03} />\n <Vignette eskil={false} offset={0.1} darkness={0.3} />\n </EffectComposer>\n )}\n </Canvas>\n\n {/* Html UI Overlays (positioned absolutely over Canvas) - matches CombatScreen pattern */}\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: Z_INDEX.HUD,\n overflow: \"clip\",\n }}\n data-testid=\"training-hud-overlay\"\n >\n {/* Left HUD - Anatomy Controls, Guard Indicator.\n Hidden on mobile because the side HUD occludes the compressed\n arena in both portrait and landscape. Anatomy layer toggles remain\n available on larger viewports where there is room for side panels. */}\n {!isMobile && (\n <TrainingLeftHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n visibleAnatomyLayers={trainingState.visibleAnatomyLayers}\n onAnatomyLayerToggle={handleAnatomyLayerToggle}\n currentStanceIndex={trainingState.currentStanceIndex}\n isInGuard={playerAnimation.isInStanceGuard()}\n />\n )}\n\n {/* Top HUD - Training Controls, Archetype Selector, Return Button. */}\n <TrainingTopHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n isTraining={trainingState.isTraining}\n onStartTraining={handleStartTraining}\n onStopTraining={handleStopTraining}\n selectedArchetype={selectedArchetype}\n onArchetypeSelect={setSelectedArchetype}\n overlayVisible={overlayVisible}\n onReturnToMenu={onReturnToMenu}\n onPlaySFX={(sound) => audio.playSFX(sound)}\n />\n\n {/* Right HUD - Mode Selector, Stats, Vital Point Selection.\n Hidden on mobile to keep the training dojang visible and usable.\n The core start/stop, archetype, vital-point toggle, technique bar,\n stance wheel, gestures, and touch controls remain available. */}\n {!isMobile && (\n <TrainingRightHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n trainingMode={trainingState.trainingMode}\n onModeChange={trainingActions.setTrainingMode}\n stats={{\n ...trainingState.stats,\n sessionDuration: trainingState.sessionDuration,\n bestCombo: trainingState.bestCombo,\n perfectStrikes: trainingState.perfectStrikes,\n }}\n distanceToDummy={distanceToDummy}\n effectiveReach={currentTechniqueReach}\n selectedVitalPoint={trainingState.selectedVitalPoint}\n onVitalPointSelect={trainingActions.setSelectedVitalPoint}\n footworkDrillType={trainingState.footworkDrillType}\n footworkDrillStep={trainingState.footworkDrillStep}\n footworkDrillActive={trainingState.footworkDrillActive}\n onStartFootworkDrill={trainingActions.startFootworkDrill}\n onStopFootworkDrill={trainingActions.stopFootworkDrill}\n onAdvanceFootworkStep={trainingActions.advanceFootworkStep}\n />\n )}\n {/* Bottom HUD - Technique Bar, Feedback Messages, Mobile Controls */}\n <TrainingBottomHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n techniques={techniqueSelection.availableTechniques}\n player={trainingPlayerState}\n selectedIndex={techniqueSelection.selectedIndex}\n cooldowns={cooldownsMap}\n onTechniqueSelect={techniqueSelection.selectTechnique}\n showFeedback={trainingState.showFeedback}\n feedbackMessage={trainingState.feedback}\n selectedArchetype={selectedArchetype}\n onArchetypeSelect={setSelectedArchetype}\n onPlaySFX={(sound) => audio.playSFX(sound)}\n />\n\n {/* Vital Point Overlay Controls - Pure DOM overlay (outside Canvas) */}\n {overlayVisible && (\n <VitalPointOverlayControlsPure\n visible={overlayVisible}\n onVisibleChange={setOverlayVisible}\n severityFilters={severityFilters}\n onSeverityFiltersChange={setSeverityFilters}\n regionFilter={regionFilter}\n onRegionFilterChange={setRegionFilter}\n searchQuery={searchQuery}\n onSearchQueryChange={setSearchQuery}\n showLabels={showLabels}\n onShowLabelsChange={setShowLabels}\n animated={animated}\n onAnimatedChange={setAnimated}\n scale={scale}\n onScaleChange={setScale}\n screenPosition={{ top: \"180px\", left: \"20px\" }}\n isMobile={isMobile}\n />\n )}\n\n {/* Mobile Controls - Pure DOM overlay (outside Canvas for reliable touch) */}\n {isMobile && (\n <>\n <MobileControlsOverlay\n onMove={handleMobileMove}\n onAttack={handleMobileAttack}\n onBlock={handleMobileBlock}\n disabled={!mobileControlsEnabled}\n bottom={getMobileControlsBottom(height)}\n opacity={0.85}\n viewportWidth={width}\n viewportHeight={height}\n />\n\n <StanceWheelPure\n currentStance={trainingState.currentStanceIndex}\n onStanceChange={handleMobileStanceChange}\n expanded={trainingState.stanceWheelExpanded}\n onToggle={trainingActions.toggleStanceWheel}\n disabled={!mobileControlsEnabled}\n opacity={0.8}\n />\n\n <GestureRecognizerPure\n onGesture={handleMobileGesture}\n enabled={mobileControlsEnabled}\n showFeedback={true}\n minSwipeDistance={50}\n />\n </>\n )}\n </div>\n </div>\n );\n};\n\nexport default TrainingScreen3D;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AnatomyControlsOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/training/components/AnatomyControlsOverlayHtml.tsx"],"sourcesContent":["/**\n * AnatomyControlsOverlayHtml - Html UI for toggling anatomy visualization layers\n * \n * Provides buttons to toggle skeleton, nerves, vascular, and surface layers\n * with consistent Korean martial arts cyberpunk theming.\n * \n * @module components/screens/training/components/AnatomyControlsOverlayHtml\n * @category Training UI\n * @korean 해부학제어오버레이\n */\n\nimport React, { useCallback, useState } from \"react\";\nimport type { AnatomyLayer } from \"./AnatomyOverlay3D\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { SPACING } from \"../../../../types/constants/ui\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport {\n formatBilingualText,\n getEnhancedKoreanOverlayStyles,\n getResponsiveSpacing,\n} from \"../../../../utils/koreanThemeHelpers\";\nimport {\n getNeonTextShadow,\n getSmoothTransition,\n getNeonGlowEffect,\n} from \"../../../../utils/visualEffects\";\nimport \"../training.css\";\n\n/**\n * Props for AnatomyControlsOverlayHtml component\n */\nexport interface AnatomyControlsOverlayHtmlProps {\n /** Currently visible anatomy layers */\n readonly visibleLayers: readonly AnatomyLayer[];\n /** Callback when layer visibility changes */\n readonly onLayerToggle: (layer: AnatomyLayer) => void;\n /** Whether on mobile device */\n readonly isMobile?: boolean;\n /** Optional width constraint from the parent HUD */\n readonly width?: number;\n}\n\n/**\n * Layer button configuration with Korean colors\n * \n * @korean 레이어설정\n */\ninterface LayerConfig {\n readonly id: AnatomyLayer;\n readonly korean: string;\n readonly english: string;\n readonly icon: string;\n readonly color: number; // Numeric hex color from KOREAN_COLORS\n}\n\nconst LAYER_CONFIGS: readonly LayerConfig[] = [\n {\n id: \"skeleton\",\n korean: \"골격\",\n english: \"Skeleton\",\n icon: \"🦴\",\n color: KOREAN_COLORS.TEXT_PRIMARY,\n },\n {\n id: \"nerves\",\n korean: \"신경\",\n english: \"Nerves\",\n icon: \"⚡\",\n color: KOREAN_COLORS.ACCENT_GOLD,\n },\n {\n id: \"vascular\",\n korean: \"혈관\",\n english: \"Vascular\",\n icon: \"❤️\",\n color: KOREAN_COLORS.ACCENT_RED,\n },\n {\n id: \"surface\",\n korean: \"표면\",\n english: \"Surface\",\n icon: \"👤\",\n color: KOREAN_COLORS.PRIMARY_CYAN,\n },\n];\n\n/**\n * AnatomyControlsOverlayHtml Component\n * UI controls for anatomy layer visibility\n */\nexport const AnatomyControlsOverlayHtml = React.memo<AnatomyControlsOverlayHtmlProps>(\n ({\n visibleLayers,\n onLayerToggle,\n isMobile = false,\n width,\n }) => {\n const [hoveredLayer, setHoveredLayer] = useState<AnatomyLayer | null>(null);\n\n const handleToggle = useCallback(\n (layer: AnatomyLayer) => {\n onLayerToggle(layer);\n },\n [onLayerToggle]\n );\n\n const panelWidth = width ?? (isMobile ? 220 : 260);\n const padding = getResponsiveSpacing(\"md\", isMobile);\n\n const panelStyle: React.CSSProperties = {\n ...getEnhancedKoreanOverlayStyles({\n opacity: 0.88,\n glowIntensity: \"medium\",\n includeGradient: false,\n includeBackdropBlur: true,\n depthLayers: 3,\n }),\n width: `${panelWidth}px`,\n maxWidth: \"100%\",\n padding: `${padding}px`,\n boxSizing: \"border-box\",\n };\n\n return (\n <div\n style={panelStyle}\n data-testid=\"anatomy-controls-html\"\n >\n {/* Header with bilingual text */}\n <div style={{ marginBottom: `${SPACING.MD}px` }}>\n <div\n style={{\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n color: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN),\n textShadow: getNeonTextShadow(KOREAN_COLORS.PRIMARY_CYAN, \"medium\"),\n transition: getSmoothTransition(\"all\", \"normal\"),\n }}\n >\n {formatBilingualText(\"해부학 표시\", \"Anatomy Display\", \"pipe\")}\n </div>\n </div>\n\n {/* Layer toggle buttons */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: `${SPACING.SM}px`,\n }}\n >\n {LAYER_CONFIGS.map((config) => {\n const isActive = visibleLayers.includes(config.id);\n const isHovered = hoveredLayer === config.id;\n const layerColor = hexToRgbaString(config.color);\n const activeBackground = hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.2);\n const inactiveBackground = hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.6);\n const inactiveBorder = hexToRgbaString(KOREAN_COLORS.TEXT_TERTIARY, 0.2);\n\n const glowEffect = (isActive || isHovered) \n ? getNeonGlowEffect(config.color, isActive ? \"strong\" : \"medium\", true)\n : undefined;\n\n return (\n <button\n key={config.id}\n onClick={() => handleToggle(config.id)}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: `${SPACING.SM}px`,\n background: isActive ? activeBackground : inactiveBackground,\n border: `2px solid ${isActive ? layerColor : inactiveBorder}`,\n borderRadius: `${SPACING.SM}px`,\n padding: isMobile ? `${SPACING.SM}px ${SPACING.SM}px` : `${SPACING.SM}px ${SPACING.MD}px`,\n cursor: \"pointer\",\n transition: getSmoothTransition(\"all\", \"normal\"),\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY),\n width: \"100%\",\n transform: isHovered ? \"scale(1.03)\" : \"scale(1)\",\n boxShadow: glowEffect,\n }}\n onMouseEnter={() => setHoveredLayer(config.id)}\n onMouseLeave={() => setHoveredLayer(null)}\n data-testid={`anatomy-layer-${config.id}`}\n aria-label={`Toggle ${config.english} layer`}\n aria-pressed={isActive}\n >\n {/* Icon */}\n <span\n style={{\n fontSize: isMobile ? \"18px\" : \"20px\",\n filter: isActive ? \"none\" : \"grayscale(100%)\",\n opacity: isActive ? 1 : 0.5,\n }}\n >\n {config.icon}\n </span>\n\n {/* Labels */}\n <div\n style={{\n flex: 1,\n textAlign: \"left\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"13px\",\n fontWeight: \"bold\",\n color: isActive ? layerColor : hexToRgbaString(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n {config.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: isActive \n ? hexToRgbaString(KOREAN_COLORS.TEXT_SECONDARY) \n : hexToRgbaString(KOREAN_COLORS.TEXT_TERTIARY, 0.6),\n }}\n >\n {config.english}\n </div>\n </div>\n\n {/* Active indicator */}\n <div\n style={{\n width: isMobile ? \"8px\" : \"10px\",\n height: isMobile ? \"8px\" : \"10px\",\n borderRadius: \"50%\",\n background: isActive ? layerColor : hexToRgbaString(KOREAN_COLORS.TEXT_TERTIARY, 0.2),\n boxShadow: isActive ? `0 0 10px ${layerColor}` : \"none\",\n }}\n />\n </button>\n );\n })}\n </div>\n\n {/* Info text */}\n <div\n style={{\n marginTop: `${SPACING.MD}px`,\n paddingTop: `${SPACING.MD}px`,\n borderTop: `1px solid ${hexToRgbaString(KOREAN_COLORS.TEXT_TERTIARY, 0.1)}`,\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: hexToRgbaString(KOREAN_COLORS.TEXT_TERTIARY),\n textAlign: \"center\",\n lineHeight: \"1.4\",\n fontFamily: FONT_FAMILY.KOREAN,\n }}\n >\n {formatBilingualText(\"클릭하여 표시/숨김\", \"Click to show/hide\", \"pipe\")}\n </div>\n </div>\n );\n},\n(prevProps, nextProps) => {\n return (\n prevProps.visibleLayers === nextProps.visibleLayers &&\n prevProps.isMobile === nextProps.isMobile &&\n prevProps.width === nextProps.width &&\n prevProps.onLayerToggle === nextProps.onLayerToggle\n );\n});\n\nAnatomyControlsOverlayHtml.displayName = \"AnatomyControlsOverlayHtml\";\n\nexport default AnatomyControlsOverlayHtml;\n"],"mappings":"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"}
1
+ {"version":3,"file":"AnatomyControlsOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/training/components/AnatomyControlsOverlayHtml.tsx"],"sourcesContent":["/**\n * AnatomyControlsOverlayHtml - Html UI for toggling anatomy visualization layers\n * \n * Provides buttons to toggle skeleton, nerves, vascular, and surface layers\n * with consistent Korean martial arts cyberpunk theming.\n * \n * @module components/screens/training/components/AnatomyControlsOverlayHtml\n * @category Training UI\n * @korean 해부학제어오버레이\n */\n\nimport React, { useCallback, useState } from \"react\";\nimport type { AnatomyLayer } from \"./AnatomyOverlay3D\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { SPACING } from \"../../../../types/constants/ui\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport {\n formatBilingualText,\n getEnhancedKoreanOverlayStyles,\n getResponsiveSpacing,\n} from \"../../../../utils/koreanThemeHelpers\";\nimport {\n getNeonTextShadow,\n getSmoothTransition,\n getNeonGlowEffect,\n} from \"../../../../utils/visualEffects\";\nimport \"../training.css\";\n\n/**\n * Props for AnatomyControlsOverlayHtml component\n */\nexport interface AnatomyControlsOverlayHtmlProps {\n /** Currently visible anatomy layers */\n readonly visibleLayers: readonly AnatomyLayer[];\n /** Callback when layer visibility changes */\n readonly onLayerToggle: (layer: AnatomyLayer) => void;\n /** Whether on mobile device */\n readonly isMobile?: boolean;\n /** Optional width constraint from the parent HUD */\n readonly width?: number;\n}\n\n/**\n * Layer button configuration with Korean colors\n * \n * @korean 레이어설정\n */\ninterface LayerConfig {\n readonly id: AnatomyLayer;\n readonly korean: string;\n readonly english: string;\n readonly icon: string;\n readonly color: number; // Numeric hex color from KOREAN_COLORS\n}\n\nconst LAYER_CONFIGS: readonly LayerConfig[] = [\n {\n id: \"skeleton\",\n korean: \"골격\",\n english: \"Skeleton\",\n icon: \"🦴\",\n color: KOREAN_COLORS.TEXT_PRIMARY,\n },\n {\n id: \"nerves\",\n korean: \"신경\",\n english: \"Nerves\",\n icon: \"⚡\",\n color: KOREAN_COLORS.ACCENT_GOLD,\n },\n {\n id: \"vascular\",\n korean: \"혈관\",\n english: \"Vascular\",\n icon: \"❤️\",\n color: KOREAN_COLORS.ACCENT_RED,\n },\n {\n id: \"surface\",\n korean: \"표면\",\n english: \"Surface\",\n icon: \"👤\",\n color: KOREAN_COLORS.PRIMARY_CYAN,\n },\n];\n\n/**\n * AnatomyControlsOverlayHtml Component\n * UI controls for anatomy layer visibility\n */\nexport const AnatomyControlsOverlayHtml = React.memo<AnatomyControlsOverlayHtmlProps>(\n ({\n visibleLayers,\n onLayerToggle,\n isMobile = false,\n width,\n }) => {\n const [hoveredLayer, setHoveredLayer] = useState<AnatomyLayer | null>(null);\n\n const handleToggle = useCallback(\n (layer: AnatomyLayer) => {\n onLayerToggle(layer);\n },\n [onLayerToggle]\n );\n\n const panelWidth = width ?? (isMobile ? 220 : 260);\n const padding = getResponsiveSpacing(\"md\", isMobile);\n\n const panelStyle: React.CSSProperties = {\n ...getEnhancedKoreanOverlayStyles({\n opacity: 0.88,\n glowIntensity: \"medium\",\n includeGradient: false,\n includeBackdropBlur: true,\n depthLayers: 3,\n }),\n width: `${panelWidth}px`,\n maxWidth: \"100%\",\n padding: `${padding}px`,\n boxSizing: \"border-box\",\n };\n\n return (\n <div\n style={panelStyle}\n data-testid=\"anatomy-controls-html\"\n >\n {/* Header with bilingual text */}\n <div style={{ marginBottom: `${SPACING.MD}px` }}>\n <div\n style={{\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n color: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN),\n textShadow: getNeonTextShadow(KOREAN_COLORS.PRIMARY_CYAN, \"medium\"),\n transition: getSmoothTransition(\"all\", \"normal\"),\n }}\n >\n {formatBilingualText(\"해부학 표시\", \"Anatomy Display\", \"pipe\")}\n </div>\n </div>\n\n {/* Layer toggle buttons */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: `${SPACING.SM}px`,\n }}\n >\n {LAYER_CONFIGS.map((config) => {\n const isActive = visibleLayers.includes(config.id);\n const isHovered = hoveredLayer === config.id;\n const layerColor = hexToRgbaString(config.color);\n const activeBackground = hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.2);\n const inactiveBackground = hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.6);\n const inactiveBorder = hexToRgbaString(KOREAN_COLORS.TEXT_TERTIARY, 0.2);\n\n const glowEffect = (isActive || isHovered) \n ? getNeonGlowEffect(config.color, isActive ? \"strong\" : \"medium\", true)\n : undefined;\n\n return (\n <button\n key={config.id}\n onClick={() => handleToggle(config.id)}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: `${SPACING.SM}px`,\n background: isActive ? activeBackground : inactiveBackground,\n border: `2px solid ${isActive ? layerColor : inactiveBorder}`,\n borderRadius: `${SPACING.SM}px`,\n padding: isMobile ? `${SPACING.SM}px ${SPACING.SM}px` : `${SPACING.SM}px ${SPACING.MD}px`,\n cursor: \"pointer\",\n transition: getSmoothTransition(\"all\", \"normal\"),\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY),\n width: \"100%\",\n transform: isHovered ? \"scale(1.03)\" : \"scale(1)\",\n boxShadow: glowEffect,\n }}\n onMouseEnter={() => setHoveredLayer(config.id)}\n onMouseLeave={() => setHoveredLayer(null)}\n data-testid={`anatomy-layer-${config.id}`}\n aria-label={`Toggle ${config.english} layer`}\n aria-pressed={isActive}\n >\n {/* Icon */}\n <span\n style={{\n fontSize: isMobile ? \"18px\" : \"20px\",\n filter: isActive ? \"none\" : \"grayscale(100%)\",\n opacity: isActive ? 1 : 0.5,\n }}\n >\n {config.icon}\n </span>\n\n {/* Labels */}\n <div\n style={{\n flex: 1,\n textAlign: \"left\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"13px\",\n fontWeight: \"bold\",\n color: isActive ? layerColor : hexToRgbaString(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n {config.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: isActive \n ? hexToRgbaString(KOREAN_COLORS.TEXT_SECONDARY) \n : hexToRgbaString(KOREAN_COLORS.TEXT_TERTIARY, 0.6),\n }}\n >\n {config.english}\n </div>\n </div>\n\n {/* Active indicator */}\n <div\n style={{\n width: isMobile ? \"8px\" : \"10px\",\n height: isMobile ? \"8px\" : \"10px\",\n borderRadius: \"50%\",\n background: isActive ? layerColor : hexToRgbaString(KOREAN_COLORS.TEXT_TERTIARY, 0.2),\n boxShadow: isActive ? `0 0 10px ${layerColor}` : \"none\",\n }}\n />\n </button>\n );\n })}\n </div>\n\n {/* Info text */}\n <div\n style={{\n marginTop: `${SPACING.MD}px`,\n paddingTop: `${SPACING.MD}px`,\n borderTop: `1px solid ${hexToRgbaString(KOREAN_COLORS.TEXT_TERTIARY, 0.1)}`,\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: hexToRgbaString(KOREAN_COLORS.TEXT_TERTIARY),\n textAlign: \"center\",\n lineHeight: \"1.4\",\n fontFamily: FONT_FAMILY.KOREAN,\n }}\n >\n {formatBilingualText(\"클릭하여 표시/숨김\", \"Click to show/hide\", \"pipe\")}\n </div>\n </div>\n );\n},\n(prevProps, nextProps) => {\n return (\n prevProps.visibleLayers === nextProps.visibleLayers &&\n prevProps.isMobile === nextProps.isMobile &&\n prevProps.width === nextProps.width &&\n prevProps.onLayerToggle === nextProps.onLayerToggle\n );\n});\n\nAnatomyControlsOverlayHtml.displayName = \"AnatomyControlsOverlayHtml\";\n\nexport default AnatomyControlsOverlayHtml;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAuDA,IAAM,gBAAwC;CAC5C;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,MAAM;EACN,OAAO,cAAc;CACvB;CACA;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,MAAM;EACN,OAAO,cAAc;CACvB;CACA;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,MAAM;EACN,OAAO,cAAc;CACvB;CACA;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,MAAM;EACN,OAAO,cAAc;CACvB;AACF;;;;;AAMA,IAAa,6BAA6B,MAAM,MAC7C,EACC,eACA,eACA,WAAW,OACX,YACI;CACN,MAAM,CAAC,cAAc,mBAAmB,SAA8B,IAAI;CAE1E,MAAM,eAAe,aAClB,UAAwB;EACvB,cAAc,KAAK;CACrB,GACA,CAAC,aAAa,CAChB;CAEA,MAAM,aAAa,UAAU,WAAW,MAAM;CAC9C,MAAM,UAAU,qBAAqB,MAAM,QAAQ;CAgBnD,OACE,qBAAC,OAAD;EACE,OAAO;GAfT,GAAG,+BAA+B;IAChC,SAAS;IACT,eAAe;IACf,iBAAiB;IACjB,qBAAqB;IACrB,aAAa;GACf,CAAC;GACD,OAAO,GAAG,WAAW;GACrB,UAAU;GACV,SAAS,GAAG,QAAQ;GACpB,WAAW;EAKF;EACP,eAAY;YAFd;GAKE,oBAAC,OAAD;IAAK,OAAO,EAAE,cAAc,GAAG,QAAQ,GAAG,IAAI;cAC5C,oBAAC,OAAD;KACE,OAAO;MACL,UAAU,WAAW,SAAS;MAC9B,YAAY;MACZ,OAAO,gBAAgB,cAAc,YAAY;MACjD,YAAY,kBAAkB,cAAc,cAAc,QAAQ;MAClE,YAAY,oBAAoB,OAAO,QAAQ;KACjD;eAEC,oBAAoB,UAAU,mBAAmB,MAAM;IACrD,CAAA;GACF,CAAA;GAGL,oBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,eAAe;KACf,KAAK,GAAG,QAAQ,GAAG;IACrB;cAEC,cAAc,KAAK,WAAW;KAC7B,MAAM,WAAW,cAAc,SAAS,OAAO,EAAE;KACjD,MAAM,YAAY,iBAAiB,OAAO;KAC1C,MAAM,aAAa,gBAAgB,OAAO,KAAK;KAC/C,MAAM,mBAAmB,gBAAgB,cAAc,cAAc,EAAG;KACxE,MAAM,qBAAqB,gBAAgB,cAAc,sBAAsB,EAAG;KAClF,MAAM,iBAAiB,gBAAgB,cAAc,eAAe,EAAG;KAEvE,MAAM,aAAc,YAAY,YAC5B,kBAAkB,OAAO,OAAO,WAAW,WAAW,UAAU,IAAI,IACpE,KAAA;KAEJ,OACE,qBAAC,UAAD;MAEE,eAAe,aAAa,OAAO,EAAE;MACrC,OAAO;OACL,SAAS;OACT,YAAY;OACZ,KAAK,GAAG,QAAQ,GAAG;OACnB,YAAY,WAAW,mBAAmB;OAC1C,QAAQ,aAAa,WAAW,aAAa;OAC7C,cAAc,GAAG,QAAQ,GAAG;OAC5B,SAAS,WAAW,GAAG,QAAQ,GAAG,KAAK,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,QAAQ,GAAG;OACtF,QAAQ;OACR,YAAY,oBAAoB,OAAO,QAAQ;OAC/C,YAAY,YAAY;OACxB,OAAO,gBAAgB,cAAc,YAAY;OACjD,OAAO;OACP,WAAW,YAAY,gBAAgB;OACvC,WAAW;MACb;MACA,oBAAoB,gBAAgB,OAAO,EAAE;MAC7C,oBAAoB,gBAAgB,IAAI;MACxC,eAAa,iBAAiB,OAAO;MACrC,cAAY,UAAU,OAAO,QAAQ;MACrC,gBAAc;gBAvBhB;OA0BE,oBAAC,QAAD;QACE,OAAO;SACL,UAAU,WAAW,SAAS;SAC9B,QAAQ,WAAW,SAAS;SAC5B,SAAS,WAAW,IAAI;QAC1B;kBAEC,OAAO;OACJ,CAAA;OAGN,qBAAC,OAAD;QACE,OAAO;SACL,MAAM;SACN,WAAW;QACb;kBAJF,CAME,oBAAC,OAAD;SACE,OAAO;UACL,UAAU,WAAW,SAAS;UAC9B,YAAY;UACZ,OAAO,WAAW,aAAa,gBAAgB,cAAc,aAAa;SAC5E;mBAEC,OAAO;QACL,CAAA,GACL,oBAAC,OAAD;SACE,OAAO;UACL,UAAU,WAAW,QAAQ;UAC7B,OAAO,WACH,gBAAgB,cAAc,cAAc,IAC5C,gBAAgB,cAAc,eAAe,EAAG;SACtD;mBAEC,OAAO;QACL,CAAA,CACF;;OAGL,oBAAC,OAAD,EACE,OAAO;QACL,OAAO,WAAW,QAAQ;QAC1B,QAAQ,WAAW,QAAQ;QAC3B,cAAc;QACd,YAAY,WAAW,aAAa,gBAAgB,cAAc,eAAe,EAAG;QACpF,WAAW,WAAW,YAAY,eAAe;OACnD,EACD,CAAA;MACK;QAzED,OAAO,EAyEN;IAEZ,CAAC;GACE,CAAA;GAGL,oBAAC,OAAD;IACE,OAAO;KACL,WAAW,GAAG,QAAQ,GAAG;KACzB,YAAY,GAAG,QAAQ,GAAG;KAC1B,WAAW,aAAa,gBAAgB,cAAc,eAAe,EAAG;KACxE,UAAU,WAAW,QAAQ;KAC7B,OAAO,gBAAgB,cAAc,aAAa;KAClD,WAAW;KACX,YAAY;KACZ,YAAY,YAAY;IAC1B;cAEC,oBAAoB,cAAc,sBAAsB,MAAM;GAC5D,CAAA;EACF;;AAET,IACC,WAAW,cAAc;CACxB,OACE,UAAU,kBAAkB,UAAU,iBACtC,UAAU,aAAa,UAAU,YACjC,UAAU,UAAU,UAAU,SAC9B,UAAU,kBAAkB,UAAU;AAE1C,CAAC;AAED,2BAA2B,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"AnatomyOverlay3D.js","names":[],"sources":["../../../../../src/components/screens/training/components/AnatomyOverlay3D.tsx"],"sourcesContent":["/**\n * AnatomyOverlay3D - Toggleable anatomy visualization layers\n *\n * Provides skeleton, nerves, vascular, and surface anatomy overlays\n * for educational training visualization\n */\n\nimport { useFrame } from \"@react-three/fiber\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\nimport * as THREE from \"three\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\n\nconst SKELETON_EMISSIVE_INTENSITY = 1.0; // Enhanced glow for skeletal structure\nconst NERVE_EMISSIVE_INTENSITY = 1.5; // Balanced for bloom without performance impact\nconst VASCULAR_EMISSIVE_INTENSITY = 2.0; // Moderate intensity for blood vessels\nconst VASCULAR_PULSE_BASE = 1.0; // Base intensity for vascular pulse animation\nconst VASCULAR_PULSE_AMPLITUDE = 0.5; // Pulse variation amplitude (max 1.5 total)\n\nconst SKELETON_MAJOR_TRANSMISSION = 0.1; // Reduced transmission for more solid bone appearance (was 0.3)\nconst SKELETON_MAJOR_THICKNESS = 0.5; // Increased thickness for realistic bone structure (was 0.3)\nconst SKELETON_LIMB_TRANSMISSION = 0.05; // Reduced transmission for limbs (was 0.2)\nconst SKELETON_LIMB_THICKNESS = 0.4; // Increased thickness for limb bones (was 0.2)\nconst VASCULAR_TRANSMISSION = 0.2; // All vascular system meshes\nconst VASCULAR_THICKNESS = 0.2;\n\n/**\n * Anatomy layer types\n */\nexport type AnatomyLayer = \"skeleton\" | \"nerves\" | \"vascular\" | \"surface\";\n\n/**\n * Props for AnatomyOverlay3D component\n */\nexport interface AnatomyOverlay3DProps {\n /** Position of the anatomy overlay (typically the dummy position) */\n readonly position: [number, number, number];\n /** Which anatomy layers to display */\n readonly visibleLayers: readonly AnatomyLayer[];\n /** Opacity of the overlay (0-1) */\n readonly opacity?: number;\n /** Whether on mobile device (reserved for future mobile-specific optimizations) */\n readonly isMobile?: boolean;\n}\n\n/**\n * Skeleton Layer Component\n * Simplified skeletal structure visualization with glass-like transmission\n */\nconst SkeletonLayer: React.FC<{ opacity: number }> = ({ opacity }) => {\n const groupRef = useRef<THREE.Group>(null);\n\n const skullGeometry = useMemo(() => new THREE.SphereGeometry(0.25, 16, 16), []);\n\n useEffect(() => {\n return () => {\n skullGeometry.dispose();\n };\n }, [skullGeometry]);\n\n useFrame((state) => {\n if (!groupRef.current) return;\n\n const pulse = Math.sin(state.clock.elapsedTime * 2) * 0.4 + 0.6;\n\n const meshes: THREE.Mesh[] = [];\n groupRef.current.traverse((child) => {\n if (\n child instanceof THREE.Mesh &&\n child.material instanceof THREE.MeshPhysicalMaterial\n ) {\n meshes.push(child);\n }\n });\n\n meshes.forEach((mesh) => {\n if (mesh.material instanceof THREE.MeshPhysicalMaterial) {\n mesh.material.emissiveIntensity = pulse;\n }\n });\n });\n\n return (\n <group ref={groupRef}>\n {/* Spine - vertical line */}\n <mesh position={[0, 1.0, -0.05]}>\n <cylinderGeometry args={[0.03, 0.03, 1.6, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity}\n transmission={SKELETON_MAJOR_TRANSMISSION}\n thickness={SKELETON_MAJOR_THICKNESS}\n roughness={0.4} // Increased roughness for bone texture (was 0.1)\n clearcoat={0.3}\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n\n {/* Skull - wireframe sphere */}\n <lineSegments position={[0, 1.6, 0]}>\n <edgesGeometry args={[skullGeometry]} />\n <lineBasicMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity}\n />\n </lineSegments>\n\n {/* Rib cage - horizontal lines */}\n {[1.3, 1.15, 1.0, 0.85, 0.7].map((y, i) => (\n <mesh key={i} position={[0, y, 0]} rotation={[0, 0, Math.PI / 2]}>\n <torusGeometry args={[0.25 - i * 0.02, 0.02, 8, 16]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity * 0.7}\n transmission={SKELETON_MAJOR_TRANSMISSION}\n thickness={SKELETON_MAJOR_THICKNESS}\n roughness={0.4} // Bone texture (consistent with spine)\n clearcoat={0.3}\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n ))}\n\n {/* Pelvis - simplified structure */}\n <mesh position={[0, 0.5, 0]} rotation={[0, 0, Math.PI / 2]}>\n <torusGeometry args={[0.25, 0.03, 8, 16]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity}\n transmission={SKELETON_MAJOR_TRANSMISSION}\n thickness={SKELETON_MAJOR_THICKNESS}\n roughness={0.4} // Bone texture (consistent with spine)\n clearcoat={0.3}\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n\n {/* Left arm bones */}\n <mesh position={[-0.4, 1.0, 0]} rotation={[0, 0, Math.PI / 6]}>\n <cylinderGeometry args={[0.02, 0.02, 0.6, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity * 0.8}\n transmission={SKELETON_LIMB_TRANSMISSION}\n thickness={SKELETON_LIMB_THICKNESS}\n roughness={0.4} // Bone texture (consistent with spine)\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n\n {/* Right arm bones */}\n <mesh position={[0.4, 1.0, 0]} rotation={[0, 0, -Math.PI / 6]}>\n <cylinderGeometry args={[0.02, 0.02, 0.6, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity * 0.8}\n transmission={SKELETON_LIMB_TRANSMISSION}\n thickness={SKELETON_LIMB_THICKNESS}\n roughness={0.4} // Bone texture (consistent with spine)\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n\n {/* Leg bones */}\n <mesh position={[-0.2, 0.3, 0]}>\n <cylinderGeometry args={[0.02, 0.02, 0.5, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity * 0.8}\n transmission={SKELETON_LIMB_TRANSMISSION}\n thickness={SKELETON_LIMB_THICKNESS}\n roughness={0.4} // Bone texture (consistent with spine)\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n <mesh position={[0.2, 0.3, 0]}>\n <cylinderGeometry args={[0.02, 0.02, 0.5, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity * 0.8}\n transmission={SKELETON_LIMB_TRANSMISSION}\n thickness={SKELETON_LIMB_THICKNESS}\n roughness={0.4} // Bone texture (consistent with spine)\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n </group>\n );\n};\n\n/**\n * Nerves Layer Component\n * Nervous system pathways visualization\n */\nconst NervesLayer: React.FC<{ opacity: number }> = ({ opacity }) => {\n const groupRef = useRef<THREE.Group>(null);\n\n useFrame((state) => {\n if (!groupRef.current) return;\n\n const pulse = Math.sin(state.clock.elapsedTime * 3) * 0.5 + 0.5;\n const targetIntensity = 1.0 + pulse * 0.5;\n\n const meshes: THREE.Mesh[] = [];\n groupRef.current.traverse((child) => {\n if (\n child instanceof THREE.Mesh &&\n child.material instanceof THREE.MeshPhysicalMaterial\n ) {\n meshes.push(child);\n }\n });\n\n meshes.forEach((mesh) => {\n if (mesh.material instanceof THREE.MeshPhysicalMaterial) {\n mesh.material.emissiveIntensity = targetIntensity;\n }\n });\n });\n\n return (\n <group ref={groupRef}>\n {/* Spinal cord - central nerve */}\n <mesh position={[0, 1.0, -0.08]}>\n <cylinderGeometry args={[0.025, 0.025, 1.6, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.SECONDARY_YELLOW}\n transparent\n opacity={opacity}\n emissive={KOREAN_COLORS.SECONDARY_YELLOW}\n emissiveIntensity={NERVE_EMISSIVE_INTENSITY}\n roughness={0.2}\n clearcoat={1.0}\n />\n </mesh>\n\n {/* Nerve branches - from spine to limbs */}\n {/* Cervical nerves (neck area) */}\n {[-0.15, 0.15].map((x, i) => (\n <mesh\n key={`cervical-${i}`}\n position={[x, 1.4, -0.08]}\n rotation={[0, 0, x > 0 ? -Math.PI / 4 : Math.PI / 4]}\n >\n <cylinderGeometry args={[0.015, 0.015, 0.2, 6]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n transparent\n opacity={opacity * 0.8}\n emissive={KOREAN_COLORS.ACCENT_GOLD}\n emissiveIntensity={1.5}\n roughness={0.2}\n clearcoat={1.0}\n />\n </mesh>\n ))}\n\n {/* Brachial plexus (arm nerves) */}\n {[-0.3, 0.3].map((x, i) => (\n <mesh\n key={`brachial-${i}`}\n position={[x, 1.1, -0.08]}\n rotation={[0, 0, x > 0 ? -Math.PI / 3 : Math.PI / 3]}\n >\n <cylinderGeometry args={[0.015, 0.01, 0.3, 6]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n transparent\n opacity={opacity * 0.7}\n emissive={KOREAN_COLORS.ACCENT_GOLD}\n emissiveIntensity={NERVE_EMISSIVE_INTENSITY}\n roughness={0.2}\n clearcoat={1.0}\n />\n </mesh>\n ))}\n\n {/* Lumbar/sacral nerves (leg nerves) */}\n {[-0.15, 0.15].map((x, i) => (\n <mesh\n key={`lumbar-${i}`}\n position={[x, 0.5, -0.08]}\n rotation={[0, 0, x > 0 ? -Math.PI / 6 : Math.PI / 6]}\n >\n <cylinderGeometry args={[0.015, 0.01, 0.4, 6]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n transparent\n opacity={opacity * 0.7}\n emissive={KOREAN_COLORS.ACCENT_GOLD}\n emissiveIntensity={NERVE_EMISSIVE_INTENSITY}\n roughness={0.2}\n clearcoat={1.0}\n />\n </mesh>\n ))}\n </group>\n );\n};\n\n/**\n * Vascular Layer Component\n * Blood vessel system visualization with glass-like transmission\n */\nconst VascularLayer: React.FC<{ opacity: number }> = ({ opacity }) => {\n const groupRef = useRef<THREE.Group>(null);\n\n useFrame((state) => {\n if (!groupRef.current) return;\n\n const pulse = Math.sin(state.clock.elapsedTime * 2) * 0.5 + 0.5;\n const targetIntensity = VASCULAR_PULSE_BASE + pulse * VASCULAR_PULSE_AMPLITUDE;\n\n const meshes: THREE.Mesh[] = [];\n groupRef.current.traverse((child) => {\n if (\n child instanceof THREE.Mesh &&\n child.material instanceof THREE.MeshPhysicalMaterial\n ) {\n meshes.push(child);\n }\n });\n\n meshes.forEach((mesh) => {\n if (mesh.material instanceof THREE.MeshPhysicalMaterial) {\n mesh.material.emissiveIntensity = targetIntensity;\n }\n });\n });\n\n return (\n <group ref={groupRef}>\n {/* Aorta - main artery */}\n <mesh position={[0, 1.0, -0.1]}>\n <cylinderGeometry args={[0.02, 0.02, 1.4, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_RED}\n transparent\n opacity={opacity}\n transmission={VASCULAR_TRANSMISSION}\n thickness={VASCULAR_THICKNESS}\n roughness={0.2}\n clearcoat={0.8}\n emissive={KOREAN_COLORS.ACCENT_RED}\n emissiveIntensity={VASCULAR_EMISSIVE_INTENSITY}\n />\n </mesh>\n\n {/* Carotid arteries (neck) */}\n {[-0.1, 0.1].map((x, i) => (\n <mesh key={`carotid-${i}`} position={[x, 1.4, -0.05]}>\n <cylinderGeometry args={[0.015, 0.015, 0.3, 6]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_RED}\n transparent\n opacity={opacity * 0.9}\n transmission={VASCULAR_TRANSMISSION}\n thickness={VASCULAR_THICKNESS}\n roughness={0.2}\n clearcoat={0.8}\n emissive={KOREAN_COLORS.ACCENT_RED}\n emissiveIntensity={VASCULAR_EMISSIVE_INTENSITY}\n />\n </mesh>\n ))}\n\n {/* Subclavian/axillary arteries (shoulder to arm) */}\n {[-0.25, 0.25].map((x, i) => (\n <mesh\n key={`subclavian-${i}`}\n position={[x, 1.15, -0.08]}\n rotation={[0, 0, x > 0 ? -Math.PI / 4 : Math.PI / 4]}\n >\n <cylinderGeometry args={[0.012, 0.012, 0.25, 6]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_RED}\n transparent\n opacity={opacity * 0.8}\n transmission={VASCULAR_TRANSMISSION}\n thickness={VASCULAR_THICKNESS}\n roughness={0.2}\n clearcoat={0.8}\n emissive={KOREAN_COLORS.ACCENT_RED}\n emissiveIntensity={VASCULAR_EMISSIVE_INTENSITY}\n />\n </mesh>\n ))}\n\n {/* Femoral arteries (legs) */}\n {[-0.15, 0.15].map((x, i) => (\n <mesh key={`femoral-${i}`} position={[x, 0.4, -0.08]}>\n <cylinderGeometry args={[0.012, 0.012, 0.35, 6]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_RED}\n transparent\n opacity={opacity * 0.8}\n transmission={VASCULAR_TRANSMISSION}\n thickness={VASCULAR_THICKNESS}\n roughness={0.2}\n clearcoat={0.8}\n emissive={KOREAN_COLORS.ACCENT_RED}\n emissiveIntensity={VASCULAR_EMISSIVE_INTENSITY}\n />\n </mesh>\n ))}\n </group>\n );\n};\n\n/**\n * Surface Layer Component\n * Surface anatomy landmarks and skin layer\n */\nconst SurfaceLayer: React.FC<{ opacity: number }> = ({ opacity }) => {\n return (\n <group>\n {/* Glass Skin Shell - Slightly larger than dummy to envelop internals */}\n <mesh position={[0, 1.6, 0]}>\n <sphereGeometry args={[0.26, 32, 32]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n roughness={0.2}\n transmission={0.9}\n thickness={0.5}\n transparent\n opacity={opacity * 0.5}\n />\n </mesh>\n\n <mesh position={[0, 1.0, 0]}>\n <capsuleGeometry args={[0.31, 0.8, 8, 16]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n roughness={0.2}\n transmission={0.9}\n thickness={0.5}\n transparent\n opacity={opacity * 0.5}\n />\n </mesh>\n\n {/* Arms */}\n <mesh position={[-0.4, 1.0, 0]} rotation={[0, 0, Math.PI / 6]}>\n <capsuleGeometry args={[0.11, 0.6, 4, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n roughness={0.2}\n transmission={0.9}\n thickness={0.5}\n transparent\n opacity={opacity * 0.5}\n />\n </mesh>\n <mesh position={[0.4, 1.0, 0]} rotation={[0, 0, -Math.PI / 6]}>\n <capsuleGeometry args={[0.11, 0.6, 4, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n roughness={0.2}\n transmission={0.9}\n thickness={0.5}\n transparent\n opacity={opacity * 0.5}\n />\n </mesh>\n\n {/* Legs */}\n <mesh position={[-0.2, 0.3, 0]}>\n <capsuleGeometry args={[0.13, 0.5, 4, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n roughness={0.2}\n transmission={0.9}\n thickness={0.5}\n transparent\n opacity={opacity * 0.5}\n />\n </mesh>\n <mesh position={[0.2, 0.3, 0]}>\n <capsuleGeometry args={[0.13, 0.5, 4, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n roughness={0.2}\n transmission={0.9}\n thickness={0.5}\n transparent\n opacity={opacity * 0.5}\n />\n </mesh>\n </group>\n );\n};\n\n/**\n * AnatomyOverlay3D Component\n * Main component that manages all anatomy layers\n */\nexport const AnatomyOverlay3D: React.FC<AnatomyOverlay3DProps> = ({\n position,\n visibleLayers,\n opacity = 0.7,\n}) => {\n const showSkeleton = useMemo(\n () => visibleLayers.includes(\"skeleton\"),\n [visibleLayers]\n );\n const showNerves = useMemo(\n () => visibleLayers.includes(\"nerves\"),\n [visibleLayers]\n );\n const showVascular = useMemo(\n () => visibleLayers.includes(\"vascular\"),\n [visibleLayers]\n );\n const showSurface = useMemo(\n () => visibleLayers.includes(\"surface\"),\n [visibleLayers]\n );\n\n return (\n <group position={position} name=\"anatomy-overlay-3d\">\n {showSkeleton && <SkeletonLayer opacity={opacity} />}\n {showNerves && <NervesLayer opacity={opacity} />}\n {showVascular && <VascularLayer opacity={opacity} />}\n {showSurface && <SurfaceLayer opacity={opacity} />}\n </group>\n );\n};\n\nexport default AnatomyOverlay3D;\n"],"mappings":"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"}
1
+ {"version":3,"file":"AnatomyOverlay3D.js","names":[],"sources":["../../../../../src/components/screens/training/components/AnatomyOverlay3D.tsx"],"sourcesContent":["/**\n * AnatomyOverlay3D - Toggleable anatomy visualization layers\n *\n * Provides skeleton, nerves, vascular, and surface anatomy overlays\n * for educational training visualization\n */\n\nimport { useFrame } from \"@react-three/fiber\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\nimport * as THREE from \"three\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\n\nconst SKELETON_EMISSIVE_INTENSITY = 1.0; // Enhanced glow for skeletal structure\nconst NERVE_EMISSIVE_INTENSITY = 1.5; // Balanced for bloom without performance impact\nconst VASCULAR_EMISSIVE_INTENSITY = 2.0; // Moderate intensity for blood vessels\nconst VASCULAR_PULSE_BASE = 1.0; // Base intensity for vascular pulse animation\nconst VASCULAR_PULSE_AMPLITUDE = 0.5; // Pulse variation amplitude (max 1.5 total)\n\nconst SKELETON_MAJOR_TRANSMISSION = 0.1; // Reduced transmission for more solid bone appearance (was 0.3)\nconst SKELETON_MAJOR_THICKNESS = 0.5; // Increased thickness for realistic bone structure (was 0.3)\nconst SKELETON_LIMB_TRANSMISSION = 0.05; // Reduced transmission for limbs (was 0.2)\nconst SKELETON_LIMB_THICKNESS = 0.4; // Increased thickness for limb bones (was 0.2)\nconst VASCULAR_TRANSMISSION = 0.2; // All vascular system meshes\nconst VASCULAR_THICKNESS = 0.2;\n\n/**\n * Anatomy layer types\n */\nexport type AnatomyLayer = \"skeleton\" | \"nerves\" | \"vascular\" | \"surface\";\n\n/**\n * Props for AnatomyOverlay3D component\n */\nexport interface AnatomyOverlay3DProps {\n /** Position of the anatomy overlay (typically the dummy position) */\n readonly position: [number, number, number];\n /** Which anatomy layers to display */\n readonly visibleLayers: readonly AnatomyLayer[];\n /** Opacity of the overlay (0-1) */\n readonly opacity?: number;\n /** Whether on mobile device (reserved for future mobile-specific optimizations) */\n readonly isMobile?: boolean;\n}\n\n/**\n * Skeleton Layer Component\n * Simplified skeletal structure visualization with glass-like transmission\n */\nconst SkeletonLayer: React.FC<{ opacity: number }> = ({ opacity }) => {\n const groupRef = useRef<THREE.Group>(null);\n\n const skullGeometry = useMemo(() => new THREE.SphereGeometry(0.25, 16, 16), []);\n\n useEffect(() => {\n return () => {\n skullGeometry.dispose();\n };\n }, [skullGeometry]);\n\n useFrame((state) => {\n if (!groupRef.current) return;\n\n const pulse = Math.sin(state.clock.elapsedTime * 2) * 0.4 + 0.6;\n\n const meshes: THREE.Mesh[] = [];\n groupRef.current.traverse((child) => {\n if (\n child instanceof THREE.Mesh &&\n child.material instanceof THREE.MeshPhysicalMaterial\n ) {\n meshes.push(child);\n }\n });\n\n meshes.forEach((mesh) => {\n if (mesh.material instanceof THREE.MeshPhysicalMaterial) {\n mesh.material.emissiveIntensity = pulse;\n }\n });\n });\n\n return (\n <group ref={groupRef}>\n {/* Spine - vertical line */}\n <mesh position={[0, 1.0, -0.05]}>\n <cylinderGeometry args={[0.03, 0.03, 1.6, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity}\n transmission={SKELETON_MAJOR_TRANSMISSION}\n thickness={SKELETON_MAJOR_THICKNESS}\n roughness={0.4} // Increased roughness for bone texture (was 0.1)\n clearcoat={0.3}\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n\n {/* Skull - wireframe sphere */}\n <lineSegments position={[0, 1.6, 0]}>\n <edgesGeometry args={[skullGeometry]} />\n <lineBasicMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity}\n />\n </lineSegments>\n\n {/* Rib cage - horizontal lines */}\n {[1.3, 1.15, 1.0, 0.85, 0.7].map((y, i) => (\n <mesh key={i} position={[0, y, 0]} rotation={[0, 0, Math.PI / 2]}>\n <torusGeometry args={[0.25 - i * 0.02, 0.02, 8, 16]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity * 0.7}\n transmission={SKELETON_MAJOR_TRANSMISSION}\n thickness={SKELETON_MAJOR_THICKNESS}\n roughness={0.4} // Bone texture (consistent with spine)\n clearcoat={0.3}\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n ))}\n\n {/* Pelvis - simplified structure */}\n <mesh position={[0, 0.5, 0]} rotation={[0, 0, Math.PI / 2]}>\n <torusGeometry args={[0.25, 0.03, 8, 16]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity}\n transmission={SKELETON_MAJOR_TRANSMISSION}\n thickness={SKELETON_MAJOR_THICKNESS}\n roughness={0.4} // Bone texture (consistent with spine)\n clearcoat={0.3}\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n\n {/* Left arm bones */}\n <mesh position={[-0.4, 1.0, 0]} rotation={[0, 0, Math.PI / 6]}>\n <cylinderGeometry args={[0.02, 0.02, 0.6, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity * 0.8}\n transmission={SKELETON_LIMB_TRANSMISSION}\n thickness={SKELETON_LIMB_THICKNESS}\n roughness={0.4} // Bone texture (consistent with spine)\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n\n {/* Right arm bones */}\n <mesh position={[0.4, 1.0, 0]} rotation={[0, 0, -Math.PI / 6]}>\n <cylinderGeometry args={[0.02, 0.02, 0.6, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity * 0.8}\n transmission={SKELETON_LIMB_TRANSMISSION}\n thickness={SKELETON_LIMB_THICKNESS}\n roughness={0.4} // Bone texture (consistent with spine)\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n\n {/* Leg bones */}\n <mesh position={[-0.2, 0.3, 0]}>\n <cylinderGeometry args={[0.02, 0.02, 0.5, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity * 0.8}\n transmission={SKELETON_LIMB_TRANSMISSION}\n thickness={SKELETON_LIMB_THICKNESS}\n roughness={0.4} // Bone texture (consistent with spine)\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n <mesh position={[0.2, 0.3, 0]}>\n <cylinderGeometry args={[0.02, 0.02, 0.5, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.WHITE_SOLID}\n transparent\n opacity={opacity * 0.8}\n transmission={SKELETON_LIMB_TRANSMISSION}\n thickness={SKELETON_LIMB_THICKNESS}\n roughness={0.4} // Bone texture (consistent with spine)\n metalness={0} // Bone is non-metallic\n emissive={KOREAN_COLORS.PRIMARY_CYAN}\n emissiveIntensity={SKELETON_EMISSIVE_INTENSITY}\n ior={1.55} // Index of refraction for bone\n sheen={0.1} // Slight sheen for bone surface\n sheenRoughness={0.9}\n />\n </mesh>\n </group>\n );\n};\n\n/**\n * Nerves Layer Component\n * Nervous system pathways visualization\n */\nconst NervesLayer: React.FC<{ opacity: number }> = ({ opacity }) => {\n const groupRef = useRef<THREE.Group>(null);\n\n useFrame((state) => {\n if (!groupRef.current) return;\n\n const pulse = Math.sin(state.clock.elapsedTime * 3) * 0.5 + 0.5;\n const targetIntensity = 1.0 + pulse * 0.5;\n\n const meshes: THREE.Mesh[] = [];\n groupRef.current.traverse((child) => {\n if (\n child instanceof THREE.Mesh &&\n child.material instanceof THREE.MeshPhysicalMaterial\n ) {\n meshes.push(child);\n }\n });\n\n meshes.forEach((mesh) => {\n if (mesh.material instanceof THREE.MeshPhysicalMaterial) {\n mesh.material.emissiveIntensity = targetIntensity;\n }\n });\n });\n\n return (\n <group ref={groupRef}>\n {/* Spinal cord - central nerve */}\n <mesh position={[0, 1.0, -0.08]}>\n <cylinderGeometry args={[0.025, 0.025, 1.6, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.SECONDARY_YELLOW}\n transparent\n opacity={opacity}\n emissive={KOREAN_COLORS.SECONDARY_YELLOW}\n emissiveIntensity={NERVE_EMISSIVE_INTENSITY}\n roughness={0.2}\n clearcoat={1.0}\n />\n </mesh>\n\n {/* Nerve branches - from spine to limbs */}\n {/* Cervical nerves (neck area) */}\n {[-0.15, 0.15].map((x, i) => (\n <mesh\n key={`cervical-${i}`}\n position={[x, 1.4, -0.08]}\n rotation={[0, 0, x > 0 ? -Math.PI / 4 : Math.PI / 4]}\n >\n <cylinderGeometry args={[0.015, 0.015, 0.2, 6]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n transparent\n opacity={opacity * 0.8}\n emissive={KOREAN_COLORS.ACCENT_GOLD}\n emissiveIntensity={1.5}\n roughness={0.2}\n clearcoat={1.0}\n />\n </mesh>\n ))}\n\n {/* Brachial plexus (arm nerves) */}\n {[-0.3, 0.3].map((x, i) => (\n <mesh\n key={`brachial-${i}`}\n position={[x, 1.1, -0.08]}\n rotation={[0, 0, x > 0 ? -Math.PI / 3 : Math.PI / 3]}\n >\n <cylinderGeometry args={[0.015, 0.01, 0.3, 6]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n transparent\n opacity={opacity * 0.7}\n emissive={KOREAN_COLORS.ACCENT_GOLD}\n emissiveIntensity={NERVE_EMISSIVE_INTENSITY}\n roughness={0.2}\n clearcoat={1.0}\n />\n </mesh>\n ))}\n\n {/* Lumbar/sacral nerves (leg nerves) */}\n {[-0.15, 0.15].map((x, i) => (\n <mesh\n key={`lumbar-${i}`}\n position={[x, 0.5, -0.08]}\n rotation={[0, 0, x > 0 ? -Math.PI / 6 : Math.PI / 6]}\n >\n <cylinderGeometry args={[0.015, 0.01, 0.4, 6]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n transparent\n opacity={opacity * 0.7}\n emissive={KOREAN_COLORS.ACCENT_GOLD}\n emissiveIntensity={NERVE_EMISSIVE_INTENSITY}\n roughness={0.2}\n clearcoat={1.0}\n />\n </mesh>\n ))}\n </group>\n );\n};\n\n/**\n * Vascular Layer Component\n * Blood vessel system visualization with glass-like transmission\n */\nconst VascularLayer: React.FC<{ opacity: number }> = ({ opacity }) => {\n const groupRef = useRef<THREE.Group>(null);\n\n useFrame((state) => {\n if (!groupRef.current) return;\n\n const pulse = Math.sin(state.clock.elapsedTime * 2) * 0.5 + 0.5;\n const targetIntensity = VASCULAR_PULSE_BASE + pulse * VASCULAR_PULSE_AMPLITUDE;\n\n const meshes: THREE.Mesh[] = [];\n groupRef.current.traverse((child) => {\n if (\n child instanceof THREE.Mesh &&\n child.material instanceof THREE.MeshPhysicalMaterial\n ) {\n meshes.push(child);\n }\n });\n\n meshes.forEach((mesh) => {\n if (mesh.material instanceof THREE.MeshPhysicalMaterial) {\n mesh.material.emissiveIntensity = targetIntensity;\n }\n });\n });\n\n return (\n <group ref={groupRef}>\n {/* Aorta - main artery */}\n <mesh position={[0, 1.0, -0.1]}>\n <cylinderGeometry args={[0.02, 0.02, 1.4, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_RED}\n transparent\n opacity={opacity}\n transmission={VASCULAR_TRANSMISSION}\n thickness={VASCULAR_THICKNESS}\n roughness={0.2}\n clearcoat={0.8}\n emissive={KOREAN_COLORS.ACCENT_RED}\n emissiveIntensity={VASCULAR_EMISSIVE_INTENSITY}\n />\n </mesh>\n\n {/* Carotid arteries (neck) */}\n {[-0.1, 0.1].map((x, i) => (\n <mesh key={`carotid-${i}`} position={[x, 1.4, -0.05]}>\n <cylinderGeometry args={[0.015, 0.015, 0.3, 6]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_RED}\n transparent\n opacity={opacity * 0.9}\n transmission={VASCULAR_TRANSMISSION}\n thickness={VASCULAR_THICKNESS}\n roughness={0.2}\n clearcoat={0.8}\n emissive={KOREAN_COLORS.ACCENT_RED}\n emissiveIntensity={VASCULAR_EMISSIVE_INTENSITY}\n />\n </mesh>\n ))}\n\n {/* Subclavian/axillary arteries (shoulder to arm) */}\n {[-0.25, 0.25].map((x, i) => (\n <mesh\n key={`subclavian-${i}`}\n position={[x, 1.15, -0.08]}\n rotation={[0, 0, x > 0 ? -Math.PI / 4 : Math.PI / 4]}\n >\n <cylinderGeometry args={[0.012, 0.012, 0.25, 6]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_RED}\n transparent\n opacity={opacity * 0.8}\n transmission={VASCULAR_TRANSMISSION}\n thickness={VASCULAR_THICKNESS}\n roughness={0.2}\n clearcoat={0.8}\n emissive={KOREAN_COLORS.ACCENT_RED}\n emissiveIntensity={VASCULAR_EMISSIVE_INTENSITY}\n />\n </mesh>\n ))}\n\n {/* Femoral arteries (legs) */}\n {[-0.15, 0.15].map((x, i) => (\n <mesh key={`femoral-${i}`} position={[x, 0.4, -0.08]}>\n <cylinderGeometry args={[0.012, 0.012, 0.35, 6]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_RED}\n transparent\n opacity={opacity * 0.8}\n transmission={VASCULAR_TRANSMISSION}\n thickness={VASCULAR_THICKNESS}\n roughness={0.2}\n clearcoat={0.8}\n emissive={KOREAN_COLORS.ACCENT_RED}\n emissiveIntensity={VASCULAR_EMISSIVE_INTENSITY}\n />\n </mesh>\n ))}\n </group>\n );\n};\n\n/**\n * Surface Layer Component\n * Surface anatomy landmarks and skin layer\n */\nconst SurfaceLayer: React.FC<{ opacity: number }> = ({ opacity }) => {\n return (\n <group>\n {/* Glass Skin Shell - Slightly larger than dummy to envelop internals */}\n <mesh position={[0, 1.6, 0]}>\n <sphereGeometry args={[0.26, 32, 32]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n roughness={0.2}\n transmission={0.9}\n thickness={0.5}\n transparent\n opacity={opacity * 0.5}\n />\n </mesh>\n\n <mesh position={[0, 1.0, 0]}>\n <capsuleGeometry args={[0.31, 0.8, 8, 16]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n roughness={0.2}\n transmission={0.9}\n thickness={0.5}\n transparent\n opacity={opacity * 0.5}\n />\n </mesh>\n\n {/* Arms */}\n <mesh position={[-0.4, 1.0, 0]} rotation={[0, 0, Math.PI / 6]}>\n <capsuleGeometry args={[0.11, 0.6, 4, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n roughness={0.2}\n transmission={0.9}\n thickness={0.5}\n transparent\n opacity={opacity * 0.5}\n />\n </mesh>\n <mesh position={[0.4, 1.0, 0]} rotation={[0, 0, -Math.PI / 6]}>\n <capsuleGeometry args={[0.11, 0.6, 4, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n roughness={0.2}\n transmission={0.9}\n thickness={0.5}\n transparent\n opacity={opacity * 0.5}\n />\n </mesh>\n\n {/* Legs */}\n <mesh position={[-0.2, 0.3, 0]}>\n <capsuleGeometry args={[0.13, 0.5, 4, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n roughness={0.2}\n transmission={0.9}\n thickness={0.5}\n transparent\n opacity={opacity * 0.5}\n />\n </mesh>\n <mesh position={[0.2, 0.3, 0]}>\n <capsuleGeometry args={[0.13, 0.5, 4, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n roughness={0.2}\n transmission={0.9}\n thickness={0.5}\n transparent\n opacity={opacity * 0.5}\n />\n </mesh>\n </group>\n );\n};\n\n/**\n * AnatomyOverlay3D Component\n * Main component that manages all anatomy layers\n */\nexport const AnatomyOverlay3D: React.FC<AnatomyOverlay3DProps> = ({\n position,\n visibleLayers,\n opacity = 0.7,\n}) => {\n const showSkeleton = useMemo(\n () => visibleLayers.includes(\"skeleton\"),\n [visibleLayers]\n );\n const showNerves = useMemo(\n () => visibleLayers.includes(\"nerves\"),\n [visibleLayers]\n );\n const showVascular = useMemo(\n () => visibleLayers.includes(\"vascular\"),\n [visibleLayers]\n );\n const showSurface = useMemo(\n () => visibleLayers.includes(\"surface\"),\n [visibleLayers]\n );\n\n return (\n <group position={position} name=\"anatomy-overlay-3d\">\n {showSkeleton && <SkeletonLayer opacity={opacity} />}\n {showNerves && <NervesLayer opacity={opacity} />}\n {showVascular && <VascularLayer opacity={opacity} />}\n {showSurface && <SurfaceLayer opacity={opacity} />}\n </group>\n );\n};\n\nexport default AnatomyOverlay3D;\n"],"mappings":"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"}