All files / components/shared/three/anatomy Foot3D.tsx

95% Statements 19/20
71.42% Branches 5/7
100% Functions 6/6
95% Lines 19/20

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 219 220 221 222 223 224 225 226 227 228 229 230 231 232                                                                                                                                                        12x               528x   216x 216x 216x     216x 216x 216x     216x   216x                       528x 216x     216x       528x   216x                                       528x 216x 216x       528x                                                                                                                                                                          
/**
 * Foot3D component with anatomically accurate foot geometry
 *
 * Renders detailed 3D foot with proper dimensions for martial arts stances
 * and kicks. Supports left/right feet with Korean skin tone coloring.
 *
 * Implements anatomically correct foot proportions:
 * - Length: ~26-29cm (varies by archetype)
 * - Width: ~10cm at widest point
 * - Height: ~8cm at ankle
 *
 * @module components/three/Foot3D
 * @category 3D Components
 * @korean 발3D컴포넌트
 */
 
import React, { useEffect, useMemo } from "react";
import * as THREE from "three";
import { KOREAN_COLORS } from "../../../../types/constants";
 
/**
 * Props for Foot3D component
 *
 * @public
 * @korean 발3D속성
 */
export interface Foot3DProps {
  /**
   * Foot side (left or right)
   * @korean 발쪽
   */
  readonly side: "left" | "right";
 
  /**
   * Base skin color
   * @korean 피부색
   */
  readonly skinColor?: number;
 
  /**
   * Scale multiplier (based on archetype physical attributes)
   * @korean 크기배율
   */
  readonly scale?: number;
 
  /**
   * Whether foot is highlighted (e.g., during kicks)
   * @korean 표시여부
   */
  readonly isHighlighted?: boolean;
}
 
/**
 * Foot3D Component
 *
 * Complete foot geometry with anatomically correct dimensions suitable
 * for Korean martial arts stance visualization and kick animations.
 *
 * Design notes:
 * - Main foot body is box-shaped with rounded edges
 * - Toe area is slightly elevated and separated
 * - Heel is wider than toe area for stability
 * - Dimensions scale with archetype (Amsalja: smaller, Jojik: larger)
 *
 * @example
 * ```tsx
 * <Foot3D
 *   side="right"
 *   skinColor={0xffdbac}
 *   scale={1.0}
 *   isHighlighted={false}
 * />
 * ```
 *
 * @korean 발3D컴포넌트
 */
export const Foot3D: React.FC<Foot3DProps> = ({
  side,
  skinColor = 0xffdbac,
  scale = 1.0,
  isHighlighted = false,
}) => {
  // Anatomically correct foot dimensions for average male (180cm height)
  // These scale with archetype physical attributes
  const footDimensions = useMemo(() => {
    // Average male foot: 26-29cm length, ~10cm width, ~8cm height
    const footLength = 0.26 * scale; // 26cm base length
    const footWidth = 0.1 * scale; // 10cm width
    const footHeight = 0.08 * scale; // 8cm height at ankle
 
    // Toe area dimensions (front 30% of foot)
    const toeLength = footLength * 0.3;
    const toeWidth = footWidth * 0.9; // Slightly narrower than heel
    const toeHeight = footHeight * 0.6; // Lower profile
 
    // Heel area dimensions (back 70% of foot)
    const heelLength = footLength * 0.7;
 
    return {
      footLength,
      footWidth,
      footHeight,
      toeLength,
      toeWidth,
      toeHeight,
      heelLength,
    };
  }, [scale]);
 
  // Foot color (highlight during kicks with brighter color)
  const footColor = useMemo(() => {
    Iif (isHighlighted) {
      return KOREAN_COLORS.ACCENT_GOLD;
    }
    return skinColor;
  }, [isHighlighted, skinColor]);
 
  // Memoize shared skin material for all foot parts
  const skinMaterial = useMemo(
    () =>
      new THREE.MeshPhysicalMaterial({
        color: footColor,
        metalness: 0,
        roughness: 0.8,
        clearcoat: 0.3,
        clearcoatRoughness: 0.5,
        // PBR skin properties
        transmission: 0,
        thickness: 0.1,
        ior: 1.4, // Index of refraction for skin
        sheen: 0.1, // Subtle skin sheen
        sheenRoughness: 0.8,
        // Subtle emissive for alive appearance
        emissive: new THREE.Color(footColor),
        emissiveIntensity: isHighlighted ? 0.3 : 0.02,
      }),
    [footColor, isHighlighted],
  );
 
  // Dispose skin material on unmount to prevent memory leaks
  useEffect(() => {
    return () => {
      skinMaterial.dispose();
    };
  }, [skinMaterial]);
 
  return (
    <group name={`foot-3d-${side}`}>
      {/* Ankle connector - larger sphere that bridges shin body surface to foot */}
      <mesh
        position={[0, 0, 0]}
        castShadow
        receiveShadow
        name={`foot-ankle-bridge-${side}`}
      >
        <sphereGeometry args={[footDimensions.footHeight * 0.6, 10, 10]} />
        <primitive object={skinMaterial} attach="material" />
      </mesh>
 
      {/* Ankle-to-heel transition cylinder */}
      <mesh
        position={[
          0,
          -footDimensions.footHeight * 0.2,
          footDimensions.heelLength * 0.1,
        ]}
        castShadow
        receiveShadow
        name={`foot-ankle-transition-${side}`}
      >
        <cylinderGeometry
          args={[
            footDimensions.footHeight * 0.5, // Top radius matches ankle sphere
            footDimensions.footWidth * 0.45, // Bottom matches heel width
            footDimensions.footHeight * 0.4,
            8,
          ]}
        />
        <primitive object={skinMaterial} attach="material" />
      </mesh>
 
      {/* Main heel/midfoot body - capsule for smooth organic shape */}
      <mesh
        position={[
          0,
          -footDimensions.footHeight * 0.4,
          footDimensions.heelLength * 0.2,
        ]}
        rotation={[Math.PI / 2, 0, 0]}
        castShadow
        receiveShadow
        name={`foot-heel-${side}`}
      >
        <capsuleGeometry
          args={[
            footDimensions.footWidth * 0.45, // Radius
            footDimensions.heelLength * 0.5, // Length (shorter than full heel - caps add length)
            4,
            8,
          ]}
        />
        <primitive object={skinMaterial} attach="material" />
      </mesh>
 
      {/* Toe area - capsule for rounded toe shape */}
      <mesh
        position={[
          0,
          -footDimensions.footHeight * 0.35 + footDimensions.toeHeight * 0.2,
          footDimensions.heelLength * 0.7 + footDimensions.toeLength / 2,
        ]}
        rotation={[Math.PI / 2, 0, 0]}
        castShadow
        receiveShadow
        name={`foot-toes-${side}`}
      >
        <capsuleGeometry
          args={[
            footDimensions.toeWidth * 0.35, // Narrower radius for tapered toes
            footDimensions.toeLength * 0.4, // Shorter length
            4,
            8,
          ]}
        />
        <primitive object={skinMaterial} attach="material" />
      </mesh>
    </group>
  );
};
 
export default Foot3D;