All files / components/screens/philosophy/components TrigramVisualization3D.tsx

80% Statements 12/15
100% Branches 3/3
57.14% Functions 4/7
80% Lines 12/15

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                                                                                                1x 14x         14x 14x   14x 112x 112x 112x     112x   112x             14x                                       112x                                                        
import { OrbitControls } from "@react-three/drei";
import React, { useState, useMemo } from "react";
import { TRIGRAM_DATA, TRIGRAM_STANCES_ORDER } from "../../../../systems/trigram/types";
import { TrigramStance } from "../../../../types";
import { KOREAN_COLORS } from "../../../../types/constants/colors";
import { TrigramSymbol3D } from "./TrigramSymbol3D";
 
export interface TrigramVisualization3DProps {
  readonly selectedTrigram: TrigramStance | null;
  readonly onTrigramSelect: (stance: TrigramStance) => void;
  readonly enableControls?: boolean;
}
 
/**
 * 3D Trigram Visualization Component
 * 
 * **Korean**: 3D 트라이그램 시각화
 * 
 * Arranges eight trigram symbols in 3D space with:
 * - Circular formation around center point
 * - Interactive selection with hover effects
 * - Smooth camera controls (optional)
 * - Lighting for dramatic effect
 * 
 * Layout:
 * - 8 trigrams arranged in a circle with 4.5 unit radius
 * - Each symbol positioned based on traditional I Ching sequence
 * - Vertical stacking for visual depth
 * 
 * Performance:
 * - Memoized positions to avoid recalculation
 * - Efficient event handling
 * - Uses TrigramSymbol3D for individual rendering
 * 
 * @example
 * ```typescript
 * <TrigramVisualization3D
 *   selectedTrigram={selectedTrigram}
 *   onTrigramSelect={(stance) => setSelectedTrigram(stance)}
 *   enableControls={true}
 * />
 * ```
 * 
 * @public
 * @category Philosophy Components
 */
export const TrigramVisualization3D: React.FC<
  TrigramVisualization3DProps
> = ({ selectedTrigram, onTrigramSelect, enableControls = false }) => {
  const [hoveredTrigram, setHoveredTrigram] = useState<TrigramStance | null>(
    null
  );
 
  // Memoize trigram positions in circular formation
  const trigramPositions = useMemo(() => {
    const radius = 4.5;
 
    return TRIGRAM_STANCES_ORDER.map((stance, index) => {
      const angle = (index / TRIGRAM_STANCES_ORDER.length) * Math.PI * 2;
      const x = Math.cos(angle) * radius;
      const z = Math.sin(angle) * radius;
      // Add vertical variation using double frequency for visual depth effect
      // This creates a wave pattern that rises and falls twice as fast as the circle
      const y = Math.sin(angle * 2) * 0.5;
 
      return {
        stance,
        position: [x, y, z] as [number, number, number],
      };
    });
  }, []);
 
  return (
    <>
      {/* Lighting setup */}
      <ambientLight intensity={0.4} />
      
      {/* Primary directional light with optional shadows */}
      <directionalLight
        position={[10, 10, 5]}
        intensity={1.2}
        castShadow
        shadow-mapSize-width={1024}
        shadow-mapSize-height={1024}
      />
 
      {/* Accent lights for cyberpunk feel */}
      <pointLight position={[0, 5, 0]} intensity={0.8} color={KOREAN_COLORS.PRIMARY_CYAN} />
      <pointLight position={[0, -2, 0]} intensity={0.5} color={KOREAN_COLORS.ACCENT_GOLD} />
 
      {/* Trigram symbols in circular formation */}
      {trigramPositions.map(({ stance, position }) => (
        <TrigramSymbol3D
          key={stance}
          trigram={TRIGRAM_DATA[stance]}
          stance={stance}
          position={position}
          isSelected={selectedTrigram === stance}
          isHovered={hoveredTrigram === stance}
          onClick={() => onTrigramSelect(stance)}
          onPointerOver={() => setHoveredTrigram(stance)}
          onPointerOut={() => setHoveredTrigram(null)}
        />
      ))}
 
      {/* Optional orbital controls for development */}
      {enableControls && (
        <OrbitControls
          enablePan={true}
          enableZoom={true}
          enableRotate={true}
          minDistance={5}
          maxDistance={20}
        />
      )}
    </>
  );
};
 
export default TrigramVisualization3D;