All files / components/shared/three/indicators TrigramSymbol3D.tsx

100% Statements 19/19
100% Branches 14/14
100% Functions 6/6
100% Lines 18/18

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                                                                                                                                                              1x                         36x 35x 33x       36x 35x 1x     34x 34x       36x 35x       36x 35x 1x       34x                   36x 36x                                 36x                                        
/**
 * TrigramSymbol3D - Reusable 3D trigram symbol component
 * 
 * Displays authentic Korean trigram symbols (☰☱☲☳☴☵☶☷) with
 * brushstroke effects and fade animations for combat feedback.
 * 
 * @module components/shared/three/indicators/TrigramSymbol3D
 * @category Combat UI
 * @korean 삼차원팔괘기호
 */
 
import { Html } from "@react-three/drei";
import React, { useMemo } from "react";
import * as THREE from "three";
import { FONT_FAMILY } from "../../../../types/constants";
 
/**
 * Props for TrigramSymbol3D component
 * @korean 삼차원팔괘기호속성
 */
export interface TrigramSymbol3DProps {
  /** Trigram symbol character (☰☱☲☳☴☵☶☷) */
  readonly symbol: string;
  /** Position in 3D space */
  readonly position: THREE.Vector3Tuple;
  /** Symbol color (hexadecimal format) */
  readonly color: number | string;
  /** Opacity (0.0 to 1.0) */
  readonly opacity?: number;
  /** Scale multiplier */
  readonly scale?: number;
  /** Whether to show Korean brushstroke effect */
  readonly brushstroke?: boolean;
  /** Font size in pixels */
  readonly fontSize?: number;
  /** Whether to use mobile-optimized sizing */
  readonly isMobile?: boolean;
  /** Additional CSS class name */
  readonly className?: string;
  /** Test ID for testing */
  readonly testId?: string;
}
 
/**
 * TrigramSymbol3D Component
 * 
 * Renders a Korean trigram symbol in 3D space using Html overlay.
 * Supports fade animations, elemental colors, and brushstroke effects.
 * 
 * Features:
 * - Authentic Unicode trigram symbols (☰☱☲☳☴☵☶☷)
 * - Korean brushstroke text-shadow effect
 * - Smooth fade in/out animations
 * - Responsive scaling for mobile/desktop
 * - Optimized for 60fps performance
 * 
 * @example
 * ```tsx
 * // Basic usage
 * <TrigramSymbol3D 
 *   symbol="☰"
 *   position={[0, 1, 0]}
 *   color={0xffffff}
 * />
 * 
 * // With fade animation
 * <TrigramSymbol3D 
 *   symbol="☲"
 *   position={[0, 1, 0]}
 *   color={0xff4444}
 *   opacity={0.7}
 *   scale={1.5}
 *   brushstroke={true}
 * />
 * ```
 * 
 * @public
 * @korean 삼차원팔괘기호컴포넌트
 */
export const TrigramSymbol3D: React.FC<TrigramSymbol3DProps> = ({
  symbol,
  position,
  color,
  opacity = 1.0,
  scale = 1.0,
  brushstroke = true,
  fontSize: customFontSize,
  isMobile = false,
  className = "",
  testId = "trigram-symbol-3d",
}) => {
  // Calculate responsive font size
  const fontSize = useMemo(() => {
    if (customFontSize) return customFontSize;
    return isMobile ? 48 : 64;
  }, [customFontSize, isMobile]);
 
  // Convert color to CSS format
  const colorString = useMemo(() => {
    if (typeof color === "string") {
      return color;
    }
    // Convert hex number to CSS hex string
    const hexString = color.toString(16).padStart(6, "0");
    return `#${hexString}`;
  }, [color]);
 
  // Calculate scaled font size
  const scaledFontSize = useMemo(() => {
    return Math.round(fontSize * scale);
  }, [fontSize, scale]);
 
  // Brushstroke text-shadow effect
  const textShadow = useMemo(() => {
    if (!brushstroke) {
      return `0 0 20px ${colorString}, 0 0 40px ${colorString}`;
    }
 
    // Multi-layered shadow for Korean brushstroke effect
    return `
      0 0 20px ${colorString},
      0 0 40px ${colorString},
      1px 1px 2px rgba(0, 0, 0, 0.8),
      -1px -1px 2px rgba(255, 255, 255, 0.1),
      2px 2px 4px rgba(0, 0, 0, 0.6)
    `.trim();
  }, [brushstroke, colorString]);
 
  // Style object
  const style = useMemo(
    () => ({
      fontSize: `${scaledFontSize}px`,
      color: colorString,
      fontFamily: FONT_FAMILY.KOREAN,
      fontWeight: "bold" as const,
      textShadow,
      opacity,
      transition: "opacity 0.3s ease-in-out, transform 0.3s ease-in-out",
      transform: `scale(${scale})`,
      userSelect: "none" as const,
      pointerEvents: "none" as const,
      WebkitFontSmoothing: "antialiased" as const,
      MozOsxFontSmoothing: "grayscale" as const,
    }),
    [scaledFontSize, colorString, textShadow, opacity, scale]
  );
 
  return (
    <Html position={position} center distanceFactor={10}>
      <div
        data-testid={testId}
        className={className}
        style={style}
        role="img"
        aria-label={`Trigram symbol ${symbol}`}
      >
        {symbol}
      </div>
    </Html>
  );
};
 
/**
 * Memoized TrigramSymbol3D to prevent unnecessary re-renders
 * @korean 메모이즈된삼차원팔괘기호
 */
export default React.memo(TrigramSymbol3D);