All files / components/shared/three/scene DebugCollision.tsx

11.11% Statements 2/18
0% Branches 0/16
0% Functions 0/6
11.76% Lines 2/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218                                                                                                        7x                                                                 7x                                                                                                                                                                                                                                                                        
/**
 * Debug visualization component for collision detection bounding boxes.
 * 
 * **Korean**: 충돌 감지 디버그 시각화
 * 
 * Provides visual debugging tools for the collision detection system, including:
 * - Bounding boxes for all 5 anatomical regions
 * - Attack reach spheres
 * - Raycaster visualization
 * 
 * @module components/three/DebugCollision
 * @category Debug Components
 * @korean 충돌디버그
 */
 
import React, { useMemo, useEffect } from "react";
import { CollisionDetection } from "../../../../systems/physics/CollisionDetection";
import { KOREAN_COLORS } from "../../../../types/constants";
import type { Position3D, AnatomicalRegionPhysics } from "../../../../types/physics";
 
/**
 * Props for DebugCollision component.
 * 
 * @public
 * @korean 디버그충돌속성
 */
export interface DebugCollisionProps {
  /** Whether to show bounding boxes */
  readonly showBoundingBoxes?: boolean;
  
  /** Whether to show attack reach sphere */
  readonly showAttackReach?: boolean;
  
  /** Attack reach radius in meters */
  readonly attackReach?: number;
  
  /** Position of the attacker (for reach sphere) */
  readonly attackerPosition?: Position3D;
  
  /** Position of the defender (for bounding boxes) */
  readonly defenderPosition?: Position3D;
  
  /** Opacity of debug visuals (0-1) */
  readonly opacity?: number;
}
 
/**
 * Color mapping for different anatomical regions.
 * 
 * @private
 * @korean 영역색상매핑
 */
const REGION_COLORS: Record<AnatomicalRegionPhysics, number> = {
  head: 0x000000,                             // Black for head
  neck: KOREAN_COLORS.ACCENT_BLUE,            // Blue for neck
  torso: KOREAN_COLORS.ACCENT_GOLD,           // Yellow/Gold for torso
  arms: KOREAN_COLORS.SECONDARY_MAGENTA,      // Magenta for arms
  legs: 0x00FF88,                             // Green for legs
};
 
/**
 * Debug visualization component for collision detection.
 * 
 * **Korean**: 충돌 감지 디버그 컴포넌트
 * 
 * Renders transparent wireframe visualizations of bounding boxes and attack reach.
 * Useful for debugging collision detection logic and tuning parameters.
 * 
 * @example
 * ```tsx
 * <Canvas>
 *   <DebugCollision
 *     showBoundingBoxes={true}
 *     showAttackReach={true}
 *     attackReach={0.77}
 *     attackerPosition={{ x: 0, y: 0, z: 5 }}
 *     defenderPosition={{ x: 0, y: 0, z: 6 }}
 *     opacity={0.3}
 *   />
 * </Canvas>
 * ```
 * 
 * @public
 * @korean 디버그충돌컴포넌트
 */
export const DebugCollision: React.FC<DebugCollisionProps> = ({
  showBoundingBoxes = true,
  showAttackReach = true,
  attackReach = 0.7,
  attackerPosition = { x: 0, y: 0, z: 5 },
  defenderPosition = { x: 0, y: 0, z: 6 },
  opacity = 0.3,
}) => {
  // Create collision detection instance
  const collision = useMemo(() => new CollisionDetection(), []);
  
  // Cleanup on unmount
  useEffect(() => {
    return () => {
      collision.dispose();
    };
  }, [collision]);
  
  // Get all bounding boxes
  const boundingBoxes = useMemo(() => {
    const boxes = collision.getAllBoundingBoxes();
    return Array.from(boxes.entries());
  }, [collision]);
  
  return (
    <group data-testid="debug-collision">
      {/* Bounding boxes for anatomical regions */}
      {showBoundingBoxes && boundingBoxes.map(([region, box]) => {
        const color = REGION_COLORS[region];
        const position: [number, number, number] = [
          defenderPosition.x + box.center.x,
          defenderPosition.y + box.center.y,
          defenderPosition.z + box.center.z,
        ];
        
        // Render different geometry based on bounding box type
        switch (box.type) {
          case "sphere":
            return (
              <mesh key={region} position={position}>
                <sphereGeometry args={[box.dimensions.x, 16, 16]} />
                <meshBasicMaterial
                  color={color}
                  wireframe
                  transparent
                  opacity={opacity}
                />
              </mesh>
            );
          
          case "box":
            return (
              <mesh key={region} position={position}>
                <boxGeometry args={[
                  box.dimensions.x,
                  box.dimensions.y,
                  box.dimensions.z,
                ]} />
                <meshBasicMaterial
                  color={color}
                  wireframe
                  transparent
                  opacity={opacity}
                />
              </mesh>
            );
          
          case "capsule":
            return (
              <mesh key={region} position={position}>
                <capsuleGeometry args={[
                  box.dimensions.x,
                  box.dimensions.y,
                  8,
                  16,
                ]} />
                <meshBasicMaterial
                  color={color}
                  wireframe
                  transparent
                  opacity={opacity}
                />
              </mesh>
            );
          
          default:
            return null;
        }
      })}
      
      {/* Attack reach sphere */}
      {showAttackReach && (
        <mesh
          position={[attackerPosition.x, attackerPosition.y, attackerPosition.z]}
          data-testid="attack-reach-sphere"
        >
          <sphereGeometry args={[attackReach, 32, 32]} />
          <meshBasicMaterial
            color={KOREAN_COLORS.PRIMARY_CYAN}
            wireframe
            transparent
            opacity={opacity * 0.5}
          />
        </mesh>
      )}
      
      {/* Attack direction ray */}
      {showAttackReach && (
        <line key="attack-direction-ray">
          <bufferGeometry>
            <bufferAttribute
              attach="attributes-position"
              count={2}
              args={[new Float32Array([
                attackerPosition.x, attackerPosition.y, attackerPosition.z,
                defenderPosition.x, defenderPosition.y, defenderPosition.z,
              ]), 3]}
            />
          </bufferGeometry>
          <lineBasicMaterial
            color={KOREAN_COLORS.ACCENT_GOLD}
            linewidth={2}
            transparent
            opacity={opacity}
          />
        </line>
      )}
    </group>
  );
};
 
export default DebugCollision;