- 스프라이트 이펙트를 왜곡 영역에서 분리하여 독립적인 영역으로 관리 - 스프라이트 시트 애니메이션 기능 추가 및 UV 제어 로직 구현 - 에디터 내 스프라이트 이펙트 영역 시각화 및 드래그 이동 기능 추가 - 이펙트 감지 방식을 다각형에서 원형(Radius) 기반으로 변경 - 관련 타입 정의 및 매니저 클래스 리팩토링
1 line
149 KiB
Plaintext
1 line
149 KiB
Plaintext
{"version":3,"sources":["../src/components/ImageDistortion.tsx","../src/engine/ThreeScene.ts","../src/engine/ShaderManager.ts","../src/utils/easing.ts","../src/utils/motionPresets.ts","../src/engine/AnimationLoop.ts","../src/engine/SpriteEffectManager.ts","../src/engine/SpriteEffectInstance.ts","../src/engine/SpriteParticlePool.ts","../src/hooks/useAnimationFrame.ts","../src/hooks/useMouseInteraction.ts","../src/hooks/useMouseVelocity.ts","../src/engine/SpringPhysics.ts","../src/utils/constants.ts","../src/editor/components/EditorCanvas.tsx","../src/editor/components/AreaList.tsx","../src/editor/components/ParameterPanel.tsx","../src/editor/hooks/useDistortionEditor.ts","../src/editor/constants.ts"],"sourcesContent":["import React, { useEffect, useRef, useState, useCallback } from 'react';\r\nimport * as THREE from 'three';\r\nimport { type DistortionArea } from '@/types';\r\nimport type { SpriteEffectArea } from '@/types/spriteEffect';\r\nimport { ThreeScene } from '@/engine/ThreeScene';\r\nimport { ShaderManager } from '@/engine/ShaderManager';\r\nimport { AnimationLoop } from '@/engine/AnimationLoop';\r\nimport { SpriteEffectManager } from '@/engine/SpriteEffectManager';\r\nimport { useAnimationFrame } from '@/hooks/useAnimationFrame';\r\nimport { useMouseInteraction } from '@/hooks/useMouseInteraction';\r\nimport { SHADER_CONFIG } from '@/utils/constants';\r\nimport { MouseInteractionConfig } from '@/types/interaction';\r\n\r\n/**\r\n * ImageDistortion 컴포넌트 Props\r\n */\r\nexport interface ImageDistortionProps {\r\n /** 이미지 소스 URL */\r\n imageSrc: string;\r\n /** 왜곡 영역 배열 */\r\n areas: DistortionArea[];\r\n /** 버텍스 셰이더 경로 (선택사항) */\r\n vertexShaderPath?: string;\r\n /** 프래그먼트 셰이더 경로 (선택사항) */\r\n fragmentShaderPath?: string;\r\n /** 컨테이너 스타일 */\r\n style?: React.CSSProperties;\r\n /** 컨테이너 클래스명 */\r\n className?: string;\r\n /** 마우스 인터랙션 설정 */\r\n mouseInteraction?: MouseInteractionConfig;\r\n /** 독립 스프라이트 이펙트 영역 (왜곡 영역과 분리) */\r\n spriteEffectAreas?: SpriteEffectArea[];\r\n}\r\n\r\n/**\r\n * GPU 가속 이미지 왜곡 컴포넌트\r\n * Three.js와 GLSL 셰이더를 사용하여 실시간 이미지 왜곡 효과를 제공합니다.\r\n */\r\nexport const ImageDistortion: React.FC<ImageDistortionProps> = ({\r\n imageSrc,\r\n areas,\r\n vertexShaderPath,\r\n fragmentShaderPath,\r\n style,\r\n className,\r\n mouseInteraction,\r\n spriteEffectAreas = [],\r\n}) => {\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const sceneRef = useRef<ThreeScene | null>(null);\r\n const shaderManagerRef = useRef<ShaderManager>(new ShaderManager());\r\n const textureRef = useRef<THREE.Texture | null>(null);\r\n const spriteManagerRef = useRef<SpriteEffectManager | null>(null);\r\n const currentAreasRef = useRef<DistortionArea[]>(areas);\r\n\r\n const [isReady, setIsReady] = useState(false);\r\n const [imageLoaded, setImageLoaded] = useState(false);\r\n const [currentAreas, setCurrentAreas] = useState<DistortionArea[]>(areas);\r\n\r\n // 마우스 인터랙션 훅\r\n const mouseInteractionHook = useMouseInteraction(\r\n containerRef,\r\n mouseInteraction || {\r\n enabled: false,\r\n physics: {\r\n stiffness: 100,\r\n damping: 10,\r\n mass: 1,\r\n influenceRadius: 0.2,\r\n maxStrength: 1.0,\r\n },\r\n }\r\n );\r\n\r\n // 영역 변경 시 상태 업데이트\r\n useEffect(() => {\r\n setCurrentAreas(areas);\r\n }, [areas]);\r\n\r\n // currentAreasRef 동기화\r\n useEffect(() => {\r\n currentAreasRef.current = currentAreas;\r\n }, [currentAreas]);\r\n\r\n // 스프라이트 이펙트 매니저 초기화\r\n useEffect(() => {\r\n if (!sceneRef.current || !isReady) return;\r\n const manager = new SpriteEffectManager();\r\n manager.attachToScene(sceneRef.current.getScene());\r\n spriteManagerRef.current = manager;\r\n return () => {\r\n manager.dispose();\r\n spriteManagerRef.current = null;\r\n };\r\n }, [isReady]);\r\n\r\n // 이펙트 영역 변경 또는 매니저 준비 시 스프라이트 이펙트 동기화\r\n useEffect(() => {\r\n spriteManagerRef.current?.syncEffects(spriteEffectAreas);\r\n }, [spriteEffectAreas, isReady]);\r\n\r\n // 마우스 인터랙션 설정 변경 시 업데이트\r\n useEffect(() => {\r\n if (mouseInteraction) {\r\n mouseInteractionHook.updateConfig(mouseInteraction);\r\n }\r\n }, [mouseInteraction, mouseInteractionHook]);\r\n\r\n // Three.js 씬 초기화\r\n useEffect(() => {\r\n console.log('[ImageDistortion] useEffect 실행, containerRef.current:', containerRef.current);\r\n\r\n if (!containerRef.current) {\r\n console.warn('[ImageDistortion] containerRef.current가 null입니다. 컴포넌트가 제대로 마운트되지 않았습니다.');\r\n return;\r\n }\r\n\r\n console.log('[ImageDistortion] 초기화 시작');\r\n const scene = new ThreeScene(containerRef.current);\r\n sceneRef.current = scene;\r\n\r\n // 셰이더 로드\r\n const vertPath = vertexShaderPath || '/shaders/distortion.vert.glsl';\r\n const fragPath = fragmentShaderPath || '/shaders/distortion.frag.glsl';\r\n\r\n console.log('[ImageDistortion] 셰이더 로드 시도:', { vertPath, fragPath });\r\n\r\n shaderManagerRef.current\r\n .loadShaders(vertPath, fragPath)\r\n .then(({ vertex, fragment }) => {\r\n console.log('[ImageDistortion] 셰이더 로드 성공');\r\n scene.setShaderMaterial(vertex, fragment);\r\n setIsReady(true);\r\n })\r\n .catch((error) => {\r\n console.error('[ImageDistortion] 셰이더 로드 실패:', error);\r\n });\r\n\r\n return () => {\r\n scene.dispose();\r\n if (textureRef.current) {\r\n textureRef.current.dispose();\r\n }\r\n };\r\n }, [vertexShaderPath, fragmentShaderPath]);\r\n\r\n // 이미지 텍스처 로드\r\n useEffect(() => {\r\n if (!imageSrc || !isReady) {\r\n console.log('[ImageDistortion] 이미지 로드 스킵:', { imageSrc, isReady });\r\n return;\r\n }\r\n\r\n console.log('[ImageDistortion] 이미지 로드 시작:', imageSrc);\r\n setImageLoaded(false);\r\n\r\n const loader = new THREE.TextureLoader();\r\n loader.load(\r\n imageSrc,\r\n (texture) => {\r\n console.log('[ImageDistortion] 이미지 로드 성공!', {\r\n width: texture.image.width,\r\n height: texture.image.height\r\n });\r\n textureRef.current = texture;\r\n setImageLoaded(true);\r\n if (sceneRef.current) {\r\n sceneRef.current.updateUniforms({\r\n u_texture: { value: texture },\r\n });\r\n sceneRef.current.render();\r\n console.log('[ImageDistortion] 텍스처 업데이트 및 렌더링 완료');\r\n }\r\n },\r\n (progress) => {\r\n console.log('[ImageDistortion] 이미지 로딩 중...',\r\n Math.round((progress.loaded / progress.total) * 100) + '%'\r\n );\r\n },\r\n (error) => {\r\n console.error('[ImageDistortion] 이미지 로드 실패:', error);\r\n setImageLoaded(false);\r\n }\r\n );\r\n\r\n return () => {\r\n if (textureRef.current) {\r\n textureRef.current.dispose();\r\n textureRef.current = null;\r\n }\r\n };\r\n }, [imageSrc, isReady]);\r\n\r\n // 셰이더 유니폼 업데이트\r\n useEffect(() => {\r\n if (!sceneRef.current || !isReady) return;\r\n\r\n // 현재 해상도 가져오기\r\n const resolution = sceneRef.current.getResolution();\r\n\r\n // 포인트 배열 생성\r\n // UI는 좌상단 (0,0), WebGL은 좌하단 (0,0)이므로 y 좌표를 반전\r\n const points = new Float32Array(SHADER_CONFIG.MAX_POINTS * 2);\r\n currentAreas.forEach((area, areaIndex) => {\r\n area.basePoints.forEach((point, pointIndex) => {\r\n const index = (areaIndex * 4 + pointIndex) * 2;\r\n points[index] = point.x;\r\n points[index + 1] = 1.0 - point.y; // y 좌표 반전\r\n });\r\n });\r\n\r\n // 드래그 벡터 배열 생성\r\n // dragVector도 y 좌표계를 맞춰야 하므로 y를 반전\r\n const dragVectors = new Float32Array(SHADER_CONFIG.MAX_DRAG_VECTORS * 2);\r\n currentAreas.forEach((area, index) => {\r\n const baseIndex = index * 2;\r\n dragVectors[baseIndex] = area.dragVector.x;\r\n dragVectors[baseIndex + 1] = -area.dragVector.y; // y 방향 반전\r\n });\r\n\r\n // 강도 배열 생성\r\n const strengths = new Float32Array(SHADER_CONFIG.MAX_STRENGTHS);\r\n currentAreas.forEach((area, index) => {\r\n strengths[index] = area.distortionStrength;\r\n });\r\n\r\n // 렌즈 효과 배열 생성\r\n const lensEffects = new Float32Array(SHADER_CONFIG.MAX_LENS_EFFECTS);\r\n currentAreas.forEach((area, index) => {\r\n lensEffects[index] = area.lensEffect?.strength ?? 0;\r\n });\r\n\r\n sceneRef.current.updateUniforms({\r\n u_numAreas: { value: currentAreas.length },\r\n u_points: { value: points },\r\n u_dragVectors: { value: dragVectors },\r\n u_distortionStrengths: { value: strengths },\r\n u_lensEffects: { value: lensEffects },\r\n });\r\n\r\n sceneRef.current.render();\r\n }, [currentAreas, isReady]);\r\n\r\n // 애니메이션 루프\r\n const animationCallback = useCallback((deltaTime: number) => {\r\n if (!isReady) return;\r\n\r\n setCurrentAreas((prevAreas) => {\r\n // 현재 인터랙션 중인 영역 인덱스 가져오기\r\n const interactingIndices = mouseInteractionHook.getInteractingAreaIndices?.() || new Set<number>();\r\n\r\n // 1. 자동 애니메이션 업데이트\r\n let updatedAreas = AnimationLoop.updateProgress(prevAreas, deltaTime);\r\n updatedAreas = AnimationLoop.updateAreaDragVectors(updatedAreas);\r\n\r\n // 인터랙션 중인 영역만 dragVector를 0으로 설정\r\n if (interactingIndices.size > 0) {\r\n updatedAreas = updatedAreas.map((area, index) => {\r\n if (interactingIndices.has(index)) {\r\n return {\r\n ...area,\r\n dragVector: { x: 0, y: 0 }\r\n };\r\n }\r\n return area;\r\n });\r\n }\r\n\r\n // 2. 마우스 인터랙션 적용 (기존 dragVector에 스프링 변위 추가)\r\n if (mouseInteraction?.enabled) {\r\n updatedAreas = mouseInteractionHook.updateInteraction(updatedAreas, deltaTime);\r\n }\r\n\r\n return updatedAreas;\r\n });\r\n\r\n // 스프라이트 이펙트 업데이트 (왜곡 영역과 독립적)\r\n if (spriteManagerRef.current) {\r\n const mouseState = mouseInteractionHook.getMouseState();\r\n spriteManagerRef.current.update(\r\n spriteEffectAreas,\r\n deltaTime,\r\n {\r\n position: mouseState.position ?? null,\r\n isDragging: mouseState.isDragging,\r\n }\r\n );\r\n\r\n // 스프라이트 메쉬 변경 후 렌더링 필요\r\n sceneRef.current?.render();\r\n }\r\n }, [isReady, mouseInteraction, mouseInteractionHook, spriteEffectAreas]);\r\n\r\n // 애니메이션 루프 실행\r\n useAnimationFrame(animationCallback, true);\r\n\r\n return (\r\n <div\r\n ref={containerRef}\r\n style={{\r\n width: '100%',\r\n height: '100%',\r\n position: 'relative',\r\n ...style,\r\n }}\r\n className={className}\r\n >\r\n {!imageLoaded && (\r\n <div\r\n style={{\r\n position: 'absolute',\r\n top: '50%',\r\n left: '50%',\r\n transform: 'translate(-50%, -50%)',\r\n background: 'rgba(0, 0, 0, 0.7)',\r\n color: 'white',\r\n padding: '20px',\r\n borderRadius: '8px',\r\n zIndex: 999,\r\n }}\r\n >\r\n 이미지 로딩 중...\r\n </div>\r\n )}\r\n </div>\r\n );\r\n};","import * as THREE from 'three';\nimport type { ShaderUniforms } from '@/types';\n\n/**\n * Three.js 씬 관리 클래스\n */\nexport class ThreeScene {\n private scene: THREE.Scene;\n private camera: THREE.OrthographicCamera;\n private renderer: THREE.WebGLRenderer;\n private mesh: THREE.Mesh | null = null;\n private uniforms: ShaderUniforms;\n\n constructor(private container: HTMLElement) {\n // 씬 생성\n this.scene = new THREE.Scene();\n\n // 2D용 직교 카메라 설정 (카메라는 -z 방향, near=0 ~ far=1)\n this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);\n\n // 렌더러 설정\n this.renderer = new THREE.WebGLRenderer({\n antialias: true,\n alpha: false,\n });\n this.renderer.setPixelRatio(window.devicePixelRatio);\n this.container.appendChild(this.renderer.domElement);\n\n // 유니폼 초기화\n this.uniforms = {\n u_resolution: { value: new THREE.Vector2() },\n u_texture: { value: null },\n u_points: { value: new Float32Array(64) }, // 32포인트 × 2(x,y)\n u_numAreas: { value: 0 },\n u_dragVectors: { value: new Float32Array(16) }, // 8벡터 × 2(x,y)\n u_distortionStrengths: { value: new Float32Array(8) },\n u_lensEffects: { value: new Float32Array(8) },\n };\n\n this.handleResize();\n window.addEventListener('resize', this.handleResize);\n }\n\n /**\n * 윈도우 리사이즈 핸들러\n */\n private handleResize = () => {\n const width = this.container.clientWidth;\n const height = this.container.clientHeight;\n\n this.renderer.setSize(width, height);\n this.uniforms.u_resolution.value.set(width, height);\n\n if (this.mesh) {\n this.render();\n }\n };\n\n /**\n * 셰이더 머티리얼 설정\n * @param vertexShader 버텍스 셰이더 소스\n * @param fragmentShader 프래그먼트 셰이더 소스\n */\n public setShaderMaterial(vertexShader: string, fragmentShader: string) {\n console.log('[ThreeScene] setShaderMaterial 호출됨');\n console.log('[ThreeScene] vertexShader 길이:', vertexShader.length);\n console.log('[ThreeScene] fragmentShader 길이:', fragmentShader.length);\n\n const geometry = new THREE.PlaneGeometry(2, 2);\n const material = new THREE.ShaderMaterial({\n uniforms: this.uniforms,\n vertexShader,\n fragmentShader,\n });\n\n console.log('[ThreeScene] ShaderMaterial 생성됨');\n\n // 셰이더 컴파일 에러 확인\n const renderer = this.renderer;\n const testScene = new THREE.Scene();\n const testMesh = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material);\n testScene.add(testMesh);\n\n try {\n renderer.compile(testScene, this.camera);\n console.log('[ThreeScene] 셰이더 컴파일 성공!');\n } catch (e) {\n console.error('[ThreeScene] 셰이더 컴파일 에러:', e);\n }\n\n if (this.mesh) {\n this.scene.remove(this.mesh);\n }\n\n this.mesh = new THREE.Mesh(geometry, material);\n this.mesh.renderOrder = 0;\n this.scene.add(this.mesh);\n console.log('[ThreeScene] mesh를 씬에 추가함');\n }\n\n /**\n * Three.js 씬 객체 반환\n */\n public getScene(): THREE.Scene {\n return this.scene;\n }\n\n /**\n * 유니폼 값 업데이트\n * @param updates 업데이트할 유니폼 값들\n */\n public updateUniforms(updates: Partial<ShaderUniforms>) {\n Object.keys(updates).forEach((key) => {\n const uniformKey = key as keyof ShaderUniforms;\n this.uniforms[uniformKey].value = updates[uniformKey]!.value;\n });\n }\n\n /**\n * 씬 렌더링\n */\n public render() {\n this.renderer.render(this.scene, this.camera);\n }\n\n /**\n * 현재 해상도 가져오기\n */\n public getResolution(): { x: number; y: number } {\n return {\n x: this.uniforms.u_resolution.value.x,\n y: this.uniforms.u_resolution.value.y,\n };\n }\n\n /**\n * 리소스 정리\n */\n public dispose() {\n window.removeEventListener('resize', this.handleResize);\n this.renderer.dispose();\n if (this.mesh) {\n this.mesh.geometry.dispose();\n (this.mesh.material as THREE.Material).dispose();\n }\n if (this.container.contains(this.renderer.domElement)) {\n this.container.removeChild(this.renderer.domElement);\n }\n }\n}","/**\n * 셰이더 파일 로딩 및 관리 클래스\n */\nexport class ShaderManager {\n private vertexShaderSource: string | null = null;\n private fragmentShaderSource: string | null = null;\n\n /**\n * 셰이더 파일들을 비동기로 로드\n * @param vertexPath 버텍스 셰이더 파일 경로\n * @param fragmentPath 프래그먼트 셰이더 파일 경로\n * @returns 로드된 셰이더 소스 코드\n */\n public async loadShaders(\n vertexPath: string,\n fragmentPath: string\n ): Promise<{ vertex: string; fragment: string }> {\n console.log('[ShaderManager] loadShaders 시작:', { vertexPath, fragmentPath });\n\n try {\n console.log('[ShaderManager] fetch 시작...');\n const [vertexResponse, fragmentResponse] = await Promise.all([\n fetch(vertexPath),\n fetch(fragmentPath),\n ]);\n\n console.log('[ShaderManager] fetch 완료:', {\n vertexStatus: vertexResponse.status,\n fragmentStatus: fragmentResponse.status\n });\n\n if (!vertexResponse.ok) {\n throw new Error(`버텍스 셰이더 로드 실패: ${vertexResponse.statusText}`);\n }\n if (!fragmentResponse.ok) {\n throw new Error(`프래그먼트 셰이더 로드 실패: ${fragmentResponse.statusText}`);\n }\n\n console.log('[ShaderManager] text() 변환 시작...');\n this.vertexShaderSource = await vertexResponse.text();\n this.fragmentShaderSource = await fragmentResponse.text();\n\n console.log('[ShaderManager] 셰이더 로드 완료!', {\n vertexLength: this.vertexShaderSource.length,\n fragmentLength: this.fragmentShaderSource.length\n });\n\n return {\n vertex: this.vertexShaderSource,\n fragment: this.fragmentShaderSource,\n };\n } catch (error) {\n console.error('[ShaderManager] 셰이더 로드 실패:', error);\n throw new Error('셰이더 로딩에 실패했습니다');\n }\n }\n\n /**\n * 버텍스 셰이더 소스 코드 반환\n */\n public getVertexShader(): string {\n if (!this.vertexShaderSource) {\n throw new Error('버텍스 셰이더가 로드되지 않았습니다');\n }\n return this.vertexShaderSource;\n }\n\n /**\n * 프래그먼트 셰이더 소스 코드 반환\n */\n public getFragmentShader(): string {\n if (!this.fragmentShaderSource) {\n throw new Error('프래그먼트 셰이더가 로드되지 않았습니다');\n }\n return this.fragmentShaderSource;\n }\n}","import { type EasingFunction } from '../types';\r\n\r\ntype EasingFunc = (t: number) => number;\r\n\r\n/**\r\n * 이징 함수 구현 맵\r\n */\r\nconst easingFunctions: Record<EasingFunction, EasingFunc> = {\r\n linear: (t) => t,\r\n\r\n easeIn: (t) => t * t,\r\n easeOut: (t) => t * (2 - t),\r\n easeInOut: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),\r\n\r\n easeInQuad: (t) => t * t,\r\n easeOutQuad: (t) => t * (2 - t),\r\n\r\n easeInCubic: (t) => t * t * t,\r\n easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),\r\n};\r\n\r\n/**\r\n * 진행도에 이징 함수를 적용\r\n * @param progress 진행도 (0.0 - 1.0)\r\n * @param easingType 적용할 이징 함수 타입\r\n * @returns 이징이 적용된 진행도 (0.0 - 1.0)\r\n */\r\nexport const applyEasing = (\r\n progress: number,\r\n easingType: EasingFunction\r\n): number => {\r\n const clampedProgress = Math.max(0, Math.min(1, progress));\r\n return easingFunctions[easingType](clampedProgress);\r\n};\r\n","import type {MotionPreset, MotionPresetDefinition, Point, RotationPresetChecker} from '../types';\r\n\r\n/**\r\n * 프리셋 레지스트리 (내장 + 커스텀)\r\n */\r\nconst presetRegistry = new Map<string, MotionPresetDefinition>();\r\n\r\n/**\r\n * 회전 프리셋 목록\r\n */\r\nconst rotationPresets = new Set<string>(['rotate-cw', 'rotate-ccw']);\r\n\r\n/**\r\n * 내장 프리셋 정의\r\n */\r\nconst BUILT_IN_PRESETS: Record<string, MotionPresetDefinition> = {\r\n\t'none': () => ({x: 0, y: 0}),\r\n\t'horizontal': (strength) => ({x: strength, y: 0}),\r\n\t'vertical': (strength) => ({x: 0, y: strength}),\r\n\t'rotate-cw': (strength) => ({x: strength, y: 0}),\r\n\t'rotate-ccw': (strength) => ({x: -strength, y: 0}),\r\n\t'pulse': (strength) => ({x: strength, y: strength}),\r\n\t'diagonal-1': (strength) => ({x: strength * 0.707, y: strength * 0.707}),\r\n\t'diagonal-2': (strength) => ({x: strength * 0.707, y: -strength * 0.707}),\r\n};\r\n\r\n// 내장 프리셋 등록\r\nObject.entries(BUILT_IN_PRESETS).forEach(([name, definition]) => {\r\n\tpresetRegistry.set(name, definition);\r\n});\r\n\r\n/**\r\n * 커스텀 모션 프리셋 등록\r\n * @param name 프리셋 이름\r\n * @param definition 프리셋 정의 함수 (strength를 받아 Point 반환)\r\n * @param options 추가 옵션\r\n * @param options.isRotation 회전 애니메이션 여부 (true면 원운동)\r\n *\r\n * @example\r\n * // 좌우 진짜 왕복 (좌↔우)\r\n * registerMotionPreset('horizontal-full', (strength) => ({\r\n * x: strength * 2, // 진폭 2배\r\n * y: 0\r\n * }));\r\n *\r\n * // 8자 모양 운동 (회전)\r\n * registerMotionPreset('figure-8', (strength) => ({\r\n * x: strength,\r\n * y: strength * 0.5\r\n * }), { isRotation: true });\r\n */\r\nexport function registerMotionPreset(\r\n\tname: string,\r\n\tdefinition: MotionPresetDefinition,\r\n\toptions?: { isRotation?: boolean }\r\n): void {\r\n\tpresetRegistry.set(name, definition);\r\n\r\n\tif (options?.isRotation) {\r\n\t\trotationPresets.add(name);\r\n\t} else {\r\n\t\trotationPresets.delete(name);\r\n\t}\r\n}\r\n\r\n/**\r\n * 여러 프리셋을 한번에 등록\r\n * @param presets 프리셋 맵 (이름 → 정의)\r\n * @param rotationPresetNames 회전 프리셋 이름 목록\r\n *\r\n * @example\r\n * registerMotionPresets({\r\n * 'horizontal-full': (s) => ({x: s * 2, y: 0}),\r\n * 'wave': (s) => ({x: s, y: s * 0.3}),\r\n * }, ['wave']); // wave는 회전 애니메이션\r\n */\r\nexport function registerMotionPresets(\r\n\tpresets: Record<string, MotionPresetDefinition>,\r\n\trotationPresetNames?: string[]\r\n): void {\r\n\tObject.entries(presets).forEach(([name, definition]) => {\r\n\t\tpresetRegistry.set(name, definition);\r\n\t});\r\n\r\n\trotationPresetNames?.forEach(name => rotationPresets.add(name));\r\n}\r\n\r\n/**\r\n * 프리셋 등록 해제\r\n * @param name 프리셋 이름\r\n * @returns 해제 성공 여부\r\n */\r\nexport function unregisterMotionPreset(name: string): boolean {\r\n\trotationPresets.delete(name);\r\n\treturn presetRegistry.delete(name);\r\n}\r\n\r\n/**\r\n * 등록된 모든 프리셋 이름 조회\r\n * @returns 프리셋 이름 배열\r\n */\r\nexport function getRegisteredPresets(): string[] {\r\n\treturn Array.from(presetRegistry.keys());\r\n}\r\n\r\n/**\r\n * 프리셋 존재 여부 확인\r\n * @param name 프리셋 이름\r\n * @returns 존재 여부\r\n */\r\nexport function hasPreset(name: string): boolean {\r\n\treturn presetRegistry.has(name);\r\n}\r\n\r\n/**\r\n * 내장 프리셋으로 초기화 (커스텀 프리셋 모두 제거)\r\n */\r\nexport function resetToBuiltInPresets(): void {\r\n\tpresetRegistry.clear();\r\n\trotationPresets.clear();\r\n\r\n\tObject.entries(BUILT_IN_PRESETS).forEach(([name, definition]) => {\r\n\t\tpresetRegistry.set(name, definition);\r\n\t});\r\n\trotationPresets.add('rotate-cw');\r\n\trotationPresets.add('rotate-ccw');\r\n}\r\n\r\n/**\r\n * 모션 프리셋을 벡터로 변환\r\n * @param preset 모션 프리셋\r\n * @param strength 모션 강도 (기본값: 0.1)\r\n * @returns 계산된 벡터 (vectorA)\r\n */\r\nexport function presetToVector(preset: MotionPreset, strength: number = 0.1): Point {\r\n\tconst definition = presetRegistry.get(preset);\r\n\r\n\tif (definition) {\r\n\t\treturn definition(strength);\r\n\t}\r\n\r\n\t// 등록되지 않은 프리셋은 none으로 처리\r\n\tconsole.warn(`Unknown motion preset: \"${preset}\". Falling back to \"none\".`);\r\n\treturn {x: 0, y: 0};\r\n}\r\n\r\n/**\r\n * 프리셋이 회전 타입인지 확인\r\n */\r\nexport function isRotationPreset(preset?: MotionPreset): boolean {\r\n\tif (!preset) return false;\r\n\treturn rotationPresets.has(preset);\r\n}\r\n\r\n/**\r\n * 커스텀 회전 프리셋 판별 함수 등록\r\n * @param checker 판별 함수\r\n * @deprecated isRotation 옵션을 registerMotionPreset에 전달하세요\r\n */\r\nexport function setRotationChecker(checker: RotationPresetChecker): void {\r\n\t// Legacy support - 기존 코드 호환성을 위해 유지\r\n\tconsole.warn('setRotationChecker is deprecated. Use registerMotionPreset with { isRotation: true } option instead.');\r\n}\r\n","import { applyEasing } from '../utils/easing';\r\nimport { presetToVector, isRotationPreset } from '../utils/motionPresets';\r\nimport type {DistortionArea, Point} from \"../types\";\r\n\r\n/**\r\n * 애니메이션 루프 관리 클래스\r\n */\r\nexport class AnimationLoop {\r\n /**\r\n * 영역들의 드래그 벡터를 현재 진행도에 따라 업데이트\r\n * @param areas 왜곡 영역 배열\r\n * @returns 업데이트된 영역 배열\r\n */\r\n public static updateAreaDragVectors(\r\n areas: DistortionArea[]\r\n ): DistortionArea[] {\r\n return areas.map((area) => {\r\n const { progress, movement } = area;\r\n\r\n // duration이 0이거나 프리셋이 'none'이면 애니메이션 없음\r\n if (movement.duration <= 0 || movement.preset === 'none') {\r\n return {\r\n ...area,\r\n dragVector: { x: 0, y: 0 },\r\n };\r\n }\r\n\r\n // 프리셋이 설정되어 있으면 프리셋 기반 벡터 사용\r\n let baseVector: Point;\r\n if (movement.preset) {\r\n const strength = movement.strength ?? 0.1;\r\n baseVector = presetToVector(movement.preset, strength);\r\n } else {\r\n // 프리셋 없으면 기존 vectorA 사용 (하위 호환성)\r\n baseVector = movement.vectorA;\r\n }\r\n\r\n // 이징 적용\r\n const easedProgress = applyEasing(progress, movement.easing);\r\n\r\n // 벡터 계산\r\n let dragVector: Point;\r\n\r\n // 스텝 양자화 (영역 속성에서 가져옴)\r\n const snapSteps = area.snapSteps ?? 0;\r\n\r\n // 회전 프리셋인 경우 원운동\r\n if (movement.preset && isRotationPreset(movement.preset)) {\r\n const radius = Math.sqrt(baseVector.x * baseVector.x + baseVector.y * baseVector.y);\r\n const direction = movement.preset === 'rotate-cw' ? 1 : -1;\r\n\r\n if (snapSteps > 0) {\r\n // 스텝 양자화: 각도를 이산적 단계로 양자화\r\n const totalAngleSteps = snapSteps * 4;\r\n const rawAngle = progress * Math.PI * 2;\r\n const quantizedAngle = Math.round(rawAngle / (Math.PI * 2) * totalAngleSteps) / totalAngleSteps * Math.PI * 2;\r\n dragVector = {\r\n x: Math.cos(quantizedAngle * direction) * radius,\r\n y: Math.sin(quantizedAngle * direction) * radius,\r\n };\r\n } else {\r\n const angle = easedProgress * Math.PI * 2;\r\n dragVector = {\r\n x: Math.cos(angle * direction) * radius,\r\n y: Math.sin(angle * direction) * radius,\r\n };\r\n }\r\n } else {\r\n // 일반 왕복 모션 (sin 기반으로 진짜 좌↔우/상↔하 왕복)\r\n // sin(0)=0 → sin(π/2)=1 → sin(π)=0 → sin(3π/2)=-1 → sin(2π)=0\r\n if (snapSteps > 0) {\r\n // 스텝 양자화: oscillation 출력을 양자화\r\n // Math.round(sin * N) / N → 한 방향 N단계, 전체 2N+1개 위치\r\n const oscillation = Math.sin(progress * Math.PI * 2);\r\n const quantized = Math.round(oscillation * snapSteps) / snapSteps;\r\n dragVector = {\r\n x: baseVector.x * quantized,\r\n y: baseVector.y * quantized,\r\n };\r\n } else {\r\n const oscillation = Math.sin(easedProgress * Math.PI * 2);\r\n dragVector = {\r\n x: baseVector.x * oscillation,\r\n y: baseVector.y * oscillation,\r\n };\r\n }\r\n }\r\n\r\n return {\r\n ...area,\r\n dragVector,\r\n };\r\n });\r\n }\r\n\r\n /**\r\n * 모든 영역의 진행도를 델타 타임만큼 업데이트\r\n * @param areas 왜곡 영역 배열\r\n * @param deltaTime 델타 타임 (초)\r\n * @returns 업데이트된 영역 배열\r\n */\r\n public static updateProgress(\r\n areas: DistortionArea[],\r\n deltaTime: number\r\n ): DistortionArea[] {\r\n return areas.map((area) => {\r\n // duration이 0이면 progress 업데이트 안 함\r\n if (area.movement.duration <= 0) {\r\n return area;\r\n }\r\n\r\n let newProgress = area.progress + deltaTime / area.movement.duration;\r\n newProgress %= 1.0; // 루프\r\n\r\n return {\r\n ...area,\r\n progress: newProgress,\r\n };\r\n });\r\n }\r\n}\r\n","import * as THREE from 'three';\r\nimport type { Point } from '@/types';\r\nimport type { SpriteEffectArea } from '@/types/spriteEffect';\r\nimport { SpriteEffectInstance } from './SpriteEffectInstance';\r\n\r\n/**\r\n * 터치/마우스 상태 (스프라이트 이펙트 전용)\r\n */\r\nexport interface SpriteEffectTouchState {\r\n\t/** 마우스/터치 위치 (정규화 좌표, null이면 미접촉) */\r\n\tposition: Point | null;\r\n\t/** 드래그 중 여부 */\r\n\tisDragging: boolean;\r\n}\r\n\r\n/**\r\n * 점이 원 안에 있는지 확인\r\n */\r\nconst isPointInCircle = (point: Point, center: Point, radius: number): boolean => {\r\n\tconst dx = point.x - center.x;\r\n\tconst dy = point.y - center.y;\r\n\treturn dx * dx + dy * dy <= radius * radius;\r\n};\r\n\r\n/**\r\n * 스프라이트 이펙트 전체 관리자\r\n * ImageDistortion 컴포넌트에서 생성하여 사용하는 최상위 진입점\r\n * 왜곡 영역(DistortionArea)과 독립적으로 이펙트 영역을 관리\r\n */\r\nexport class SpriteEffectManager {\r\n\t/** 모든 이펙트 메쉬를 담는 그룹 */\r\n\tprivate effectGroup: THREE.Group;\r\n\t/** 영역ID+이펙트ID → 인스턴스 맵 */\r\n\tprivate instances: Map<string, SpriteEffectInstance> = new Map();\r\n\t/** 이전 프레임에서 터치 중이던 영역 ID 세트 (버스트 감지용) */\r\n\tprivate previousTouchingAreas: Set<string> = new Set();\r\n\r\n\tconstructor() {\r\n\t\tthis.effectGroup = new THREE.Group();\r\n\t\tthis.effectGroup.renderOrder = 1;\r\n\t}\r\n\r\n\t/**\r\n\t * Three.js 씬에 이펙트 그룹 추가\r\n\t */\r\n\tattachToScene(scene: THREE.Scene): void {\r\n\t\tscene.add(this.effectGroup);\r\n\t}\r\n\r\n\t/**\r\n\t * 이펙트 영역 설정 변경을 감지하여 인스턴스 생성/제거\r\n\t */\r\n\tsyncEffects(effectAreas: SpriteEffectArea[]): void {\r\n\t\tconsole.log('[SpriteEffectManager] syncEffects 호출:', effectAreas.length, '개 영역');\r\n\t\tconst activeKeys = new Set<string>();\r\n\r\n\t\tfor (const area of effectAreas) {\r\n\t\t\tfor (const effectConfig of area.effects) {\r\n\t\t\t\tconst key = `${area.id}::${effectConfig.id}`;\r\n\t\t\t\tactiveKeys.add(key);\r\n\r\n\t\t\t\t// 이미 존재하면 스킵\r\n\t\t\t\tif (this.instances.has(key)) continue;\r\n\r\n\t\t\t\t// 새 인스턴스 생성\r\n\t\t\t\tconsole.log('[SpriteEffectManager] 인스턴스 생성:', key, effectConfig.spriteUrl);\r\n\t\t\t\tconst instance = new SpriteEffectInstance(effectConfig);\r\n\t\t\t\tthis.instances.set(key, instance);\r\n\t\t\t\tthis.effectGroup.add(instance.group);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// 더 이상 사용되지 않는 인스턴스 제거\r\n\t\tfor (const [key, instance] of this.instances) {\r\n\t\t\tif (!activeKeys.has(key)) {\r\n\t\t\t\tinstance.dispose();\r\n\t\t\t\tthis.effectGroup.remove(instance.group);\r\n\t\t\t\tthis.instances.delete(key);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 매 프레임 업데이트\r\n\t * @param effectAreas 이펙트 영역 배열\r\n\t * @param deltaTime 초 단위 프레임 시간\r\n\t * @param touchState 마우스/터치 상태\r\n\t */\r\n\tupdate(effectAreas: SpriteEffectArea[], deltaTime: number, touchState: SpriteEffectTouchState): void {\r\n\t\t// 현재 터치 중인 영역 감지\r\n\t\tconst currentTouchingAreas = new Set<string>();\r\n\r\n\t\tif (touchState.isDragging && touchState.position) {\r\n\t\t\tfor (const area of effectAreas) {\r\n\t\t\t\tconst radius = area.radius ?? 0.1;\r\n\t\t\t\tif (isPointInCircle(touchState.position, area.position, radius)) {\r\n\t\t\t\t\tcurrentTouchingAreas.add(area.id);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// 각 영역의 이펙트 업데이트\r\n\t\tfor (const area of effectAreas) {\r\n\t\t\tfor (const effectConfig of area.effects) {\r\n\t\t\t\tconst key = `${area.id}::${effectConfig.id}`;\r\n\t\t\t\tconst instance = this.instances.get(key);\r\n\t\t\t\tif (!instance) continue;\r\n\r\n\t\t\t\t// touch 이펙트: 새로 터치된 영역이면 버스트\r\n\t\t\t\tif (effectConfig.trigger === 'touch') {\r\n\t\t\t\t\tconst isNewTouch = currentTouchingAreas.has(area.id)\r\n\t\t\t\t\t\t&& !this.previousTouchingAreas.has(area.id);\r\n\t\t\t\t\tif (isNewTouch) {\r\n\t\t\t\t\t\tinstance.triggerBurst(touchState.position ?? area.position);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// 매 프레임 업데이트 (ambient 방출 + 파티클 물리)\r\n\t\t\t\tinstance.update(deltaTime, area.position);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// 터치 상태 갱신\r\n\t\tthis.previousTouchingAreas = currentTouchingAreas;\r\n\t}\r\n\r\n\t/**\r\n\t * 리소스 정리\r\n\t */\r\n\tdispose(): void {\r\n\t\tfor (const [, instance] of this.instances) {\r\n\t\t\tinstance.dispose();\r\n\t\t}\r\n\t\tthis.instances.clear();\r\n\t\tthis.previousTouchingAreas.clear();\r\n\r\n\t\t// effectGroup을 부모에서 제거\r\n\t\tif (this.effectGroup.parent) {\r\n\t\t\tthis.effectGroup.parent.remove(this.effectGroup);\r\n\t\t}\r\n\t}\r\n}\r\n","import * as THREE from 'three';\r\nimport type { Point } from '@/types';\r\nimport type { SpriteEffectConfig, SpriteSheetConfig } from '@/types/spriteEffect';\r\nimport { SpriteParticlePool, type SpriteParticle } from './SpriteParticlePool';\r\n\r\n/**\r\n * 범위 내 랜덤 값 생성\r\n */\r\nconst randomRange = (min: number, max: number): number =>\r\n\tmin + Math.random() * (max - min);\r\n\r\n/**\r\n * 선형 보간\r\n */\r\nconst lerp = (a: number, b: number, t: number): number =>\r\n\ta + (b - a) * t;\r\n\r\n/**\r\n * 단일 SpriteEffectConfig에 대응하는 런타임 이펙트 인스턴스\r\n * 텍스처 로딩, 메쉬 풀링, 방출/업데이트 로직을 관리\r\n */\r\nexport class SpriteEffectInstance {\r\n\tprivate config: SpriteEffectConfig;\r\n\tprivate pool: SpriteParticlePool;\r\n\tprivate meshes: THREE.Mesh[];\r\n\tprivate geometry: THREE.PlaneGeometry;\r\n\tprivate material: THREE.MeshBasicMaterial;\r\n\tprivate texture: THREE.Texture | null = null;\r\n\tprivate ready = false;\r\n\tprivate emitAccumulator = 0;\r\n\r\n\t/** 메쉬를 담는 그룹 (외부에서 씬에 추가) */\r\n\treadonly group: THREE.Group;\r\n\r\n\tconstructor(config: SpriteEffectConfig) {\r\n\t\tthis.config = config;\r\n\t\tthis.pool = new SpriteParticlePool(config.maxParticles);\r\n\t\tthis.group = new THREE.Group();\r\n\r\n\t\t// 공유 지오메트리 (1x1 평면)\r\n\t\tthis.geometry = new THREE.PlaneGeometry(1, 1);\r\n\r\n\t\t// 블렌드 모드 결정\r\n\t\tconst blending = config.blendMode === 'additive'\r\n\t\t\t? THREE.AdditiveBlending\r\n\t\t\t: THREE.NormalBlending;\r\n\r\n\t\t// 공유 머티리얼 (텍스처 로드 전까지 투명)\r\n\t\tthis.material = new THREE.MeshBasicMaterial({\r\n\t\t\ttransparent: true,\r\n\t\t\tdepthTest: false,\r\n\t\t\tdepthWrite: false,\r\n\t\t\tblending,\r\n\t\t\topacity: 0,\r\n\t\t});\r\n\r\n\t\t// 메쉬 풀 사전 생성\r\n\t\tthis.meshes = Array.from({ length: config.maxParticles }, () => {\r\n\t\t\tconst mesh = new THREE.Mesh(this.geometry, this.material.clone());\r\n\t\t\tmesh.visible = false;\r\n\t\t\tmesh.renderOrder = 1;\r\n\t\t\tthis.group.add(mesh);\r\n\t\t\treturn mesh;\r\n\t\t});\r\n\r\n\t\t// 텍스처 비동기 로드\r\n\t\tthis.loadTexture(config.spriteUrl);\r\n\t}\r\n\r\n\t/** 텍스처 로드 */\r\n\tprivate loadTexture(url: string): void {\r\n\t\tconst loader = new THREE.TextureLoader();\r\n\t\tloader.load(\r\n\t\t\turl,\r\n\t\t\t(texture) => {\r\n\t\t\t\tthis.texture = texture;\r\n\r\n\t\t\t\tconst sheet = this.config.spriteSheet;\r\n\t\t\t\tif (sheet) {\r\n\t\t\t\t\t// 스프라이트 시트: 첫 프레임만 표시하도록 UV 설정\r\n\t\t\t\t\ttexture.repeat.set(1 / sheet.columns, 1 / sheet.rows);\r\n\t\t\t\t\ttexture.offset.set(0, 1 - 1 / sheet.rows);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// 각 메쉬에 독립적인 텍스처 클론 적용 (프레임별 UV 독립 제어)\r\n\t\t\t\tfor (const mesh of this.meshes) {\r\n\t\t\t\t\tconst mat = mesh.material as THREE.MeshBasicMaterial;\r\n\t\t\t\t\tif (sheet) {\r\n\t\t\t\t\t\tconst cloned = texture.clone();\r\n\t\t\t\t\t\tcloned.repeat.copy(texture.repeat);\r\n\t\t\t\t\t\tcloned.offset.copy(texture.offset);\r\n\t\t\t\t\t\tmat.map = cloned;\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tmat.map = texture;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tmat.needsUpdate = true;\r\n\t\t\t\t}\r\n\t\t\t\tthis.ready = true;\r\n\t\t\t\tconsole.log(`[SpriteEffectInstance] 텍스처 로드 성공: ${url}`, texture.image.width, 'x', texture.image.height);\r\n\t\t\t},\r\n\t\t\tundefined,\r\n\t\t\t(error) => {\r\n\t\t\t\tconsole.error(`[SpriteEffectInstance] 텍스처 로드 실패: ${url}`, error);\r\n\t\t\t}\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * 파티클 1개 방출\r\n\t * @param center 방출 중심 (정규화 좌표 0-1)\r\n\t */\r\n\tprivate emitOne(center: Point): void {\r\n\t\tconst particle = this.pool.acquire();\r\n\t\tif (!particle) return;\r\n\r\n\t\tconst { config } = this;\r\n\r\n\t\t// 방출 위치 계산 (중심 + 오프셋 + 반경 내 랜덤)\r\n\t\tlet px = center.x + (config.emitOffset?.x ?? 0);\r\n\t\tlet py = center.y + (config.emitOffset?.y ?? 0);\r\n\r\n\t\tif (config.emitRadius && config.emitRadius > 0) {\r\n\t\t\tconst angle = Math.random() * Math.PI * 2;\r\n\t\t\tconst radius = Math.random() * config.emitRadius;\r\n\t\t\tpx += Math.cos(angle) * radius;\r\n\t\t\tpy += Math.sin(angle) * radius;\r\n\t\t}\r\n\r\n\t\tparticle.position.x = px;\r\n\t\tparticle.position.y = py;\r\n\r\n\t\t// 방출 각도 및 속도\r\n\t\tconst angleRange = config.emitAngle ?? [0, 360];\r\n\t\tconst angleDeg = randomRange(angleRange[0], angleRange[1]);\r\n\t\tconst angleRad = (angleDeg * Math.PI) / 180;\r\n\t\tconst speed = randomRange(config.initialSpeed[0], config.initialSpeed[1]);\r\n\r\n\t\tparticle.velocity.x = Math.cos(angleRad) * speed;\r\n\t\tparticle.velocity.y = Math.sin(angleRad) * speed;\r\n\r\n\t\t// 초기 속성\r\n\t\tparticle.scale = randomRange(config.initialScale[0], config.initialScale[1]);\r\n\t\tparticle.rotation = 0;\r\n\t\tparticle.opacity = 1;\r\n\t\tparticle.lifetime = randomRange(config.lifetime[0], config.lifetime[1]);\r\n\t\tparticle.age = 0;\r\n\t\tparticle.frameTime = 0;\r\n\t\tparticle.frameIndex = 0;\r\n\t}\r\n\r\n\t/**\r\n\t * ambient 모드: 매 프레임 누적기 기반 방출\r\n\t */\r\n\tprivate updateAmbientEmit(deltaTime: number, center: Point): void {\r\n\t\tif (!this.config.emitRate || this.config.emitRate <= 0) return;\r\n\r\n\t\tthis.emitAccumulator += deltaTime;\r\n\t\tconst interval = 1 / this.config.emitRate;\r\n\r\n\t\twhile (this.emitAccumulator >= interval) {\r\n\t\t\tthis.emitAccumulator -= interval;\r\n\t\t\tthis.emitOne(center);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * touch 모드: 버스트 방출\r\n\t */\r\n\ttriggerBurst(center: Point): void {\r\n\t\tif (!this.ready) return;\r\n\t\tconst count = this.config.burstCount ?? 1;\r\n\t\tfor (let i = 0; i < count; i++) {\r\n\t\t\tthis.emitOne(center);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 매 프레임 업데이트\r\n\t * @param deltaTime 초 단위 프레임 시간\r\n\t * @param emitCenter 방출 중심 (정규화 좌표 0-1)\r\n\t */\r\n\tprivate _logCounter = 0;\r\n\r\n\tupdate(deltaTime: number, emitCenter: Point): void {\r\n\t\tif (!this.ready) return;\r\n\r\n\t\t// ambient 방출\r\n\t\tif (this.config.trigger === 'ambient') {\r\n\t\t\tthis.updateAmbientEmit(deltaTime, emitCenter);\r\n\t\t}\r\n\r\n\t\tconst overLifetime = this.config.overLifetime;\r\n\r\n\t\t// 활성 파티클 업데이트\r\n\t\tconst activeParticles = this.pool.getActiveParticles();\r\n\t\tif (this._logCounter++ % 60 === 0 && activeParticles.length > 0) {\r\n\t\t\tconst p = activeParticles[0];\r\n\t\t\tconsole.log(`[SpriteEffectInstance] 활성 파티클: ${activeParticles.length}개, 첫 파티클 pos=(${p.position.x.toFixed(3)}, ${p.position.y.toFixed(3)}), scale=${p.scale.toFixed(3)}, opacity=${p.opacity.toFixed(3)}`);\r\n\t\t}\r\n\t\tfor (const particle of activeParticles) {\r\n\t\t\tparticle.age += deltaTime;\r\n\r\n\t\t\t// 수명 초과 시 회수\r\n\t\t\tif (particle.age >= particle.lifetime) {\r\n\t\t\t\tthis.pool.release(particle);\r\n\t\t\t\tthis.syncMesh(particle);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tconst lifeRatio = particle.age / particle.lifetime;\r\n\r\n\t\t\t// overLifetime 보간 적용\r\n\t\t\tif (overLifetime) {\r\n\t\t\t\tif (overLifetime.scale) {\r\n\t\t\t\t\tparticle.scale = lerp(overLifetime.scale[0], overLifetime.scale[1], lifeRatio);\r\n\t\t\t\t}\r\n\t\t\t\tif (overLifetime.opacity) {\r\n\t\t\t\t\tparticle.opacity = lerp(overLifetime.opacity[0], overLifetime.opacity[1], lifeRatio);\r\n\t\t\t\t}\r\n\t\t\t\tif (overLifetime.rotationSpeed) {\r\n\t\t\t\t\tparticle.rotation += overLifetime.rotationSpeed * deltaTime;\r\n\t\t\t\t}\r\n\t\t\t\tif (overLifetime.velocityDamping !== undefined) {\r\n\t\t\t\t\tconst damping = Math.pow(overLifetime.velocityDamping, deltaTime);\r\n\t\t\t\t\tparticle.velocity.x *= damping;\r\n\t\t\t\t\tparticle.velocity.y *= damping;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// 스프라이트 시트 프레임 진행\r\n\t\t\tif (this.config.spriteSheet) {\r\n\t\t\t\tthis.updateSpriteFrame(particle, deltaTime, this.config.spriteSheet);\r\n\t\t\t}\r\n\r\n\t\t\t// 위치 업데이트\r\n\t\t\tparticle.position.x += particle.velocity.x * deltaTime;\r\n\t\t\tparticle.position.y += particle.velocity.y * deltaTime;\r\n\r\n\t\t\t// Three.js 메쉬 동기화\r\n\t\t\tthis.syncMesh(particle);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 스프라이트 시트 프레임 진행\r\n\t */\r\n\tprivate updateSpriteFrame(particle: SpriteParticle, deltaTime: number, sheet: SpriteSheetConfig): void {\r\n\t\tparticle.frameTime += deltaTime;\r\n\t\tconst frameDuration = 1 / sheet.fps;\r\n\r\n\t\tif (particle.frameTime >= frameDuration) {\r\n\t\t\tparticle.frameTime -= frameDuration;\r\n\t\t\tconst nextFrame = particle.frameIndex + 1;\r\n\r\n\t\t\tif (nextFrame >= sheet.totalFrames) {\r\n\t\t\t\t// 루프 여부 확인 (기본: true)\r\n\t\t\t\tif (sheet.loop !== false) {\r\n\t\t\t\t\tparticle.frameIndex = 0;\r\n\t\t\t\t}\r\n\t\t\t\t// loop=false면 마지막 프레임 유지\r\n\t\t\t} else {\r\n\t\t\t\tparticle.frameIndex = nextFrame;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 파티클 상태를 Three.js 메쉬에 동기화\r\n\t * 정규화 좌표(0-1) → NDC(-1~1) 변환, y축 반전\r\n\t */\r\n\tprivate syncMesh(particle: SpriteParticle): void {\r\n\t\tconst mesh = this.meshes[particle.index];\r\n\t\tif (!mesh) return;\r\n\r\n\t\tif (!particle.active) {\r\n\t\t\tmesh.visible = false;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tmesh.visible = true;\r\n\r\n\t\t// 좌표 변환: 정규화(0-1) → NDC(-1~1), y 반전\r\n\t\tmesh.position.x = particle.position.x * 2 - 1;\r\n\t\tmesh.position.y = -(particle.position.y * 2 - 1);\r\n\t\tmesh.position.z = -0.01; // 카메라가 -z를 바라보므로 음수가 앞쪽\r\n\r\n\t\tmesh.scale.set(particle.scale, particle.scale, 1);\r\n\t\tmesh.rotation.z = particle.rotation;\r\n\r\n\t\tconst mat = mesh.material as THREE.MeshBasicMaterial;\r\n\t\tmat.opacity = particle.opacity;\r\n\r\n\t\t// 스프라이트 시트 UV 오프셋 업데이트\r\n\t\tconst sheet = this.config.spriteSheet;\r\n\t\tif (sheet && mat.map) {\r\n\t\t\tconst col = particle.frameIndex % sheet.columns;\r\n\t\t\tconst row = Math.floor(particle.frameIndex / sheet.columns);\r\n\t\t\tmat.map.offset.set(\r\n\t\t\t\tcol / sheet.columns,\r\n\t\t\t\t1 - (row + 1) / sheet.rows,\r\n\t\t\t);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 텍스처 로딩 완료 여부\r\n\t */\r\n\tisReady(): boolean {\r\n\t\treturn this.ready;\r\n\t}\r\n\r\n\t/**\r\n\t * 리소스 정리\r\n\t */\r\n\tdispose(): void {\r\n\t\tif (this.texture) {\r\n\t\t\tthis.texture.dispose();\r\n\t\t\tthis.texture = null;\r\n\t\t}\r\n\t\tthis.geometry.dispose();\r\n\t\tfor (const mesh of this.meshes) {\r\n\t\t\t(mesh.material as THREE.MeshBasicMaterial).dispose();\r\n\t\t}\r\n\t\tthis.material.dispose();\r\n\t\t// 그룹에서 모든 메쉬 제거\r\n\t\twhile (this.group.children.length > 0) {\r\n\t\t\tthis.group.remove(this.group.children[0]);\r\n\t\t}\r\n\t}\r\n}\r\n","import type { Point } from '@/types';\r\n\r\n/**\r\n * 파티클 상태 데이터\r\n */\r\nexport interface SpriteParticle {\r\n\t/** 풀 내 인덱스 */\r\n\tindex: number;\r\n\t/** 활성 여부 */\r\n\tactive: boolean;\r\n\t/** 정규화 좌표 위치 (0-1) */\r\n\tposition: Point;\r\n\t/** 속도 (정규화 좌표/초) */\r\n\tvelocity: Point;\r\n\t/** 현재 스케일 */\r\n\tscale: number;\r\n\t/** 현재 회전 (라디안) */\r\n\trotation: number;\r\n\t/** 현재 투명도 */\r\n\topacity: number;\r\n\t/** 경과 시간 (초) */\r\n\tage: number;\r\n\t/** 수명 (초) */\r\n\tlifetime: number;\r\n\t/** 스프라이트 시트 프레임 누적 시간 */\r\n\tframeTime: number;\r\n\t/** 현재 프레임 인덱스 */\r\n\tframeIndex: number;\r\n}\r\n\r\n/**\r\n * 파티클 오브젝트 풀 (GC 방지)\r\n * 미리 할당된 파티클을 재활용하여 메모리 할당/해제를 최소화\r\n */\r\nexport class SpriteParticlePool {\r\n\tprivate particles: SpriteParticle[];\r\n\r\n\tconstructor(maxParticles: number) {\r\n\t\t// 최대 개수만큼 미리 할당\r\n\t\tthis.particles = Array.from({ length: maxParticles }, (_, i) => this.createParticle(i));\r\n\t}\r\n\r\n\t/** 비활성 파티클 생성 */\r\n\tprivate createParticle(index: number): SpriteParticle {\r\n\t\treturn {\r\n\t\t\tindex,\r\n\t\t\tactive: false,\r\n\t\t\tposition: { x: 0, y: 0 },\r\n\t\t\tvelocity: { x: 0, y: 0 },\r\n\t\t\tscale: 1,\r\n\t\t\trotation: 0,\r\n\t\t\topacity: 1,\r\n\t\t\tage: 0,\r\n\t\t\tlifetime: 1,\r\n\t\t\tframeTime: 0,\r\n\t\t\tframeIndex: 0,\r\n\t\t};\r\n\t}\r\n\r\n\t/**\r\n\t * 비활성 파티클을 활성화하여 반환\r\n\t * 사용 가능한 파티클이 없으면 null 반환\r\n\t */\r\n\tacquire(): SpriteParticle | null {\r\n\t\tfor (const particle of this.particles) {\r\n\t\t\tif (!particle.active) {\r\n\t\t\t\tparticle.active = true;\r\n\t\t\t\tparticle.age = 0;\r\n\t\t\t\treturn particle;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/**\r\n\t * 파티클을 비활성화하여 풀로 반환\r\n\t */\r\n\trelease(particle: SpriteParticle): void {\r\n\t\tparticle.active = false;\r\n\t}\r\n\r\n\t/**\r\n\t * 활성 파티클 목록 반환\r\n\t */\r\n\tgetActiveParticles(): SpriteParticle[] {\r\n\t\treturn this.particles.filter(p => p.active);\r\n\t}\r\n\r\n\t/**\r\n\t * 활성 파티클 수\r\n\t */\r\n\tgetActiveCount(): number {\r\n\t\tlet count = 0;\r\n\t\tfor (const p of this.particles) {\r\n\t\t\tif (p.active) count++;\r\n\t\t}\r\n\t\treturn count;\r\n\t}\r\n}\r\n","import { useEffect, useRef } from 'react';\n\n/**\n * requestAnimationFrame을 사용한 애니메이션 루프 훅\n * @param callback 매 프레임마다 호출될 콜백 (deltaTime을 인자로 받음)\n * @param isPlaying 애니메이션 재생 여부\n */\nexport const useAnimationFrame = (\n callback: (deltaTime: number) => void,\n isPlaying: boolean = true\n) => {\n const requestRef = useRef<number | undefined>(undefined);\n const previousTimeRef = useRef<number | undefined>(undefined);\n\n useEffect(() => {\n if (!isPlaying) return;\n\n const animate = (time: number) => {\n if (previousTimeRef.current !== undefined) {\n const deltaTime = (time - previousTimeRef.current) / 1000; // 밀리초를 초로 변환\n callback(deltaTime);\n }\n previousTimeRef.current = time;\n requestRef.current = requestAnimationFrame(animate);\n };\n\n requestRef.current = requestAnimationFrame(animate);\n\n return () => {\n if (requestRef.current) {\n cancelAnimationFrame(requestRef.current);\n }\n };\n }, [callback, isPlaying]);\n};","import { useRef, useCallback, useState } from 'react';\r\nimport { useMouseVelocity } from './useMouseVelocity';\r\nimport { SpringPhysics } from '@/engine/SpringPhysics';\r\nimport { DistortionArea, Point } from '@/types';\r\nimport { MouseInteractionConfig } from '@/types/interaction';\r\n\r\n/**\r\n * 점이 사각형 내부에 있는지 확인\r\n */\r\nconst isPointInPolygon = (point: Point, polygon: Point[]): boolean => {\r\n\tlet inside = false;\r\n\tfor (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {\r\n\t\tconst xi = polygon[i].x, yi = polygon[i].y;\r\n\t\tconst xj = polygon[j].x, yj = polygon[j].y;\r\n\t\tconst intersect = ((yi > point.y) !== (yj > point.y))\r\n\t\t\t&& (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);\r\n\t\tif (intersect) inside = !inside;\r\n\t}\r\n\treturn inside;\r\n};\r\n\r\n/**\r\n * 마우스 인터랙션 기반 기존 영역 제어 훅\r\n * 마우스가 지나가는 모든 영역에 효과 적용\r\n */\r\nexport const useMouseInteraction = (\r\n\tcontainerRef: React.RefObject<HTMLElement | null>,\r\n\tconfig: MouseInteractionConfig\r\n) => {\r\n\tconst { getState } = useMouseVelocity(containerRef);\r\n\tconst [interactingAreaIndices, setInteractingAreaIndices] = useState<Set<number>>(new Set());\r\n\tconst springPhysicsMapRef = useRef<Map<number, SpringPhysics>>(new Map());\r\n\r\n\t/**\r\n\t * 특정 영역의 스프링 물리 엔진 가져오기 (없으면 생성)\r\n\t */\r\n\tconst getSpringPhysics = useCallback((areaIndex: number, area?: DistortionArea): SpringPhysics => {\r\n\t\tif (!springPhysicsMapRef.current.has(areaIndex)) {\r\n\t\t\t// 영역별 물리 설정이 있으면 사용, 없으면 전역 설정 사용\r\n\t\t\tconst physicsConfig = area?.physics || config.physics;\r\n\t\t\tspringPhysicsMapRef.current.set(areaIndex, new SpringPhysics(physicsConfig));\r\n\t\t}\r\n\t\treturn springPhysicsMapRef.current.get(areaIndex)!;\r\n\t}, [config.physics]);\r\n\r\n\t/**\r\n\t * 기존 영역들의 dragVector를 마우스 인터랙션으로 업데이트\r\n\t */\r\n\tconst updateInteraction = useCallback((areas: DistortionArea[], deltaTime: number): DistortionArea[] => {\r\n\t\tif (!config.enabled) return areas;\r\n\r\n\t\t// 영역별 물리 설정이 변경되었을 수 있으므로 모든 스프링 업데이트\r\n\t\tareas.forEach((area, index) => {\r\n\t\t\tif (area.physics && springPhysicsMapRef.current.has(index)) {\r\n\t\t\t\tconst spring = springPhysicsMapRef.current.get(index)!;\r\n\t\t\t\tspring.setConfig(area.physics);\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\tconst mouseState = getState();\r\n\r\n\t\t// 마우스 클릭/드래그 중이고 위치가 있으면\r\n\t\tif (mouseState.isDragging && mouseState.position) {\r\n\t\t\t// 현재 마우스 위치가 포함된 모든 영역 찾기\r\n\t\t\tconst currentlyInAreas = new Set<number>();\r\n\t\t\tfor (let i = 0; i < areas.length; i++) {\r\n\t\t\t\tif (isPointInPolygon(mouseState.position, areas[i].basePoints)) {\r\n\t\t\t\t\tcurrentlyInAreas.add(i);\r\n\r\n\t\t\t\t\t// 새로 진입한 영역이면 스프링 리셋\r\n\t\t\t\t\tif (!interactingAreaIndices.has(i)) {\r\n\t\t\t\t\t\tgetSpringPhysics(i, areas[i]).reset();\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// 이전에 인터랙션하던 영역에서 벗어났으면 평형으로 복귀\r\n\t\t\tinteractingAreaIndices.forEach((areaIndex) => {\r\n\t\t\t\tif (!currentlyInAreas.has(areaIndex)) {\r\n\t\t\t\t\tgetSpringPhysics(areaIndex, areas[areaIndex]).returnToEquilibrium();\r\n\t\t\t\t}\r\n\t\t\t});\r\n\r\n\t\t\t// 인터랙션 영역 업데이트\r\n\t\t\tsetInteractingAreaIndices(currentlyInAreas);\r\n\r\n\t\t\t// 현재 위치의 모든 영역에 속도 적용\r\n\t\t\tconst velocityMult = config.velocityMultiplier || 1.0;\r\n\t\t\tconst velocityMag = Math.sqrt(\r\n\t\t\t\tmouseState.velocity.x ** 2 + mouseState.velocity.y ** 2\r\n\t\t\t);\r\n\t\t\tconst minVel = config.minVelocity || 0.05;\r\n\t\t\tconst maxVel = config.maxVelocity || 5.0;\r\n\r\n\t\t\t// 속도 클램핑\r\n\t\t\tlet clampedVelocity = mouseState.velocity;\r\n\t\t\tif (velocityMag > maxVel) {\r\n\t\t\t\tconst scale = maxVel / velocityMag;\r\n\t\t\t\tclampedVelocity = {\r\n\t\t\t\t\tx: mouseState.velocity.x * scale,\r\n\t\t\t\t\ty: mouseState.velocity.y * scale,\r\n\t\t\t\t};\r\n\t\t\t}\r\n\r\n\t\t\tcurrentlyInAreas.forEach((areaIndex) => {\r\n\t\t\t\tconst spring = getSpringPhysics(areaIndex, areas[areaIndex]);\r\n\r\n\t\t\t\tif (velocityMag >= minVel) {\r\n\t\t\t\t\t// 드래그 중: 마우스 속도를 목표로 설정\r\n\t\t\t\t\tspring.setTarget(clampedVelocity, velocityMult);\r\n\t\t\t\t} else {\r\n\t\t\t\t\t// 드래그 중이지만 마우스가 멈춰있으면 평형으로 복귀\r\n\t\t\t\t\tspring.returnToEquilibrium();\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t} else {\r\n\t\t\t// 마우스를 놓았으면 인터랙션 중이던 모든 영역에 튕김 효과\r\n\t\t\tif (interactingAreaIndices.size > 0) {\r\n\t\t\t\tconst velocityMult = config.velocityMultiplier || 1.0;\r\n\t\t\t\tconst maxVel = config.maxVelocity || 5.0;\r\n\r\n\t\t\t\t// 속도 클램핑\r\n\t\t\t\tconst velocityMag = Math.sqrt(\r\n\t\t\t\t\tmouseState.velocity.x ** 2 + mouseState.velocity.y ** 2\r\n\t\t\t\t);\r\n\t\t\t\tlet clampedVelocity = mouseState.velocity;\r\n\t\t\t\tif (velocityMag > maxVel) {\r\n\t\t\t\t\tconst scale = maxVel / velocityMag;\r\n\t\t\t\t\tclampedVelocity = {\r\n\t\t\t\t\t\tx: mouseState.velocity.x * scale,\r\n\t\t\t\t\t\ty: mouseState.velocity.y * scale,\r\n\t\t\t\t\t};\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// 모든 인터랙션 영역에 초기 속도 설정\r\n\t\t\t\tinteractingAreaIndices.forEach((areaIndex) => {\r\n\t\t\t\t\tconst spring = getSpringPhysics(areaIndex, areas[areaIndex]);\r\n\t\t\t\t\tspring.setInitialVelocity(clampedVelocity, velocityMult);\r\n\t\t\t\t});\r\n\r\n\t\t\t\tsetInteractingAreaIndices(new Set());\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// 모든 영역의 스프링 물리 업데이트\r\n\t\treturn areas.map((area, index) => {\r\n\t\t\tconst spring = springPhysicsMapRef.current.get(index);\r\n\t\t\tif (!spring) return area;\r\n\r\n\t\t\t// 현재 드래그 중인 영역이거나 스프링이 활성 상태일 때만 업데이트\r\n\t\t\tconst springVelocity = spring.getVelocity();\r\n\t\t\tconst springDisplacement = spring.getDisplacement();\r\n\t\t\tconst isSpringActive = Math.sqrt(springVelocity.x ** 2 + springVelocity.y ** 2) > 0.001 ||\r\n\t\t\t\tMath.sqrt(springDisplacement.x ** 2 + springDisplacement.y ** 2) > 0.001;\r\n\r\n\t\t\t// 드래그 중이 아니고 스프링도 비활성이면 업데이트 안 함\r\n\t\t\tif (!interactingAreaIndices.has(index) && !isSpringActive) {\r\n\t\t\t\treturn area;\r\n\t\t\t}\r\n\r\n\t\t\t// 스프링 물리 업데이트\r\n\t\t\tconst displacement = spring.update(deltaTime);\r\n\r\n\t\t\t// 변위가 거의 0이면 원래 dragVector 유지\r\n\t\t\tconst displacementMag = Math.sqrt(displacement.x ** 2 + displacement.y ** 2);\r\n\t\t\tif (displacementMag < 0.001) {\r\n\t\t\t\treturn area;\r\n\t\t\t}\r\n\r\n\t\t\t// 스프링 변위를 dragVector에 추가 (기존 애니메이션과 혼합)\r\n\t\t\t// dragVector는 텍스처 샘플링 좌표 이동이므로,\r\n\t\t\t// 마우스 드래그 방향과 반대로 적용해야 이미지가 드래그 방향으로 밀림\r\n\t\t\t// 예: 우→좌 드래그(velocity < 0) → dragVector > 0 → 이미지 왼쪽으로 밀림\r\n\t\t\treturn {\r\n\t\t\t\t...area,\r\n\t\t\t\tdragVector: {\r\n\t\t\t\t\tx: area.dragVector.x - displacement.x,\r\n\t\t\t\t\ty: area.dragVector.y - displacement.y,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\t\t});\r\n\t}, [config, getState, interactingAreaIndices, getSpringPhysics]);\r\n\r\n\t/**\r\n\t * 물리 파라미터 업데이트\r\n\t */\r\n\tconst updateConfig = useCallback((newConfig: Partial<MouseInteractionConfig>) => {\r\n\t\tconst physicsConfig = newConfig.physics;\r\n\t\tif (physicsConfig) {\r\n\t\t\t// 모든 스프링 물리 엔진의 설정 업데이트\r\n\t\t\tspringPhysicsMapRef.current.forEach((spring) => {\r\n\t\t\t\tspring.setConfig(physicsConfig);\r\n\t\t\t});\r\n\t\t}\r\n\t}, []);\r\n\r\n\t/**\r\n\t * 모든 영역의 스프링 상태 리셋\r\n\t */\r\n\tconst reset = useCallback(() => {\r\n\t\tspringPhysicsMapRef.current.forEach((spring) => {\r\n\t\t\tspring.reset();\r\n\t\t});\r\n\t\tsetInteractingAreaIndices(new Set());\r\n\t}, []);\r\n\r\n\t/**\r\n\t * 현재 드래그 중인지 확인\r\n\t */\r\n\tconst isDragging = useCallback((): boolean => {\r\n\t\tconst mouseState = getState();\r\n\t\treturn mouseState.isDragging;\r\n\t}, [getState]);\r\n\r\n\t/**\r\n\t * 현재 인터랙션 중인 영역 인덱스 가져오기\r\n\t */\r\n\tconst getInteractingAreaIndices = useCallback((): Set<number> => {\r\n\t\treturn interactingAreaIndices;\r\n\t}, [interactingAreaIndices]);\r\n\r\n\treturn {\r\n\t\tupdateInteraction,\r\n\t\tupdateConfig,\r\n\t\treset,\r\n\t\tisDragging,\r\n\t\tgetInteractingAreaIndices,\r\n\t\tgetMouseState: getState,\r\n\t};\r\n};\r\n","import { useRef, useCallback, useEffect } from 'react';\nimport { Point } from '@/types';\nimport { MouseState } from '@/types/interaction';\n\n/**\n * 마우스 위치, 속도, 가속도를 추적하는 훅\n */\nexport const useMouseVelocity = (containerRef: React.RefObject<HTMLElement | null>) => {\n\tconst mouseStateRef = useRef<MouseState>({\n\t\tposition: null,\n\t\tprevPosition: null,\n\t\tvelocity: { x: 0, y: 0 },\n\t\tacceleration: { x: 0, y: 0 },\n\t\tisHovering: false,\n\t\tisDragging: false,\n\t});\n\n\tconst lastUpdateTimeRef = useRef<number>(Date.now());\n\tconst prevVelocityRef = useRef<Point>({ x: 0, y: 0 });\n\n\t/**\n\t * 픽셀 좌표를 정규화 좌표(0-1)로 변환\n\t */\n\tconst toNormalized = useCallback((clientX: number, clientY: number): Point | null => {\n\t\tif (!containerRef.current) return null;\n\t\tconst rect = containerRef.current.getBoundingClientRect();\n\t\treturn {\n\t\t\tx: (clientX - rect.left) / rect.width,\n\t\t\ty: (clientY - rect.top) / rect.height,\n\t\t};\n\t}, [containerRef]);\n\n\t/**\n\t * 위치 업데이트 (마우스/터치 공통 로직)\n\t */\n\tconst updatePosition = useCallback((clientX: number, clientY: number) => {\n\t\tconst now = Date.now();\n\t\tconst deltaTime = (now - lastUpdateTimeRef.current) / 1000; // 초 단위\n\t\tlastUpdateTimeRef.current = now;\n\n\t\tconst normalizedPos = toNormalized(clientX, clientY);\n\t\tif (!normalizedPos) return;\n\n\t\tconst state = mouseStateRef.current;\n\t\tconst prevPos = state.position;\n\n\t\t// 속도 계산 (변위 / 시간)\n\t\tlet velocity: Point = { x: 0, y: 0 };\n\t\tif (prevPos && deltaTime > 0) {\n\t\t\tvelocity = {\n\t\t\t\tx: (normalizedPos.x - prevPos.x) / deltaTime,\n\t\t\t\ty: (normalizedPos.y - prevPos.y) / deltaTime,\n\t\t\t};\n\t\t}\n\n\t\t// 가속도 계산 (속도 변화 / 시간)\n\t\tconst prevVel = prevVelocityRef.current;\n\t\tlet acceleration: Point = { x: 0, y: 0 };\n\t\tif (deltaTime > 0) {\n\t\t\tacceleration = {\n\t\t\t\tx: (velocity.x - prevVel.x) / deltaTime,\n\t\t\t\ty: (velocity.y - prevVel.y) / deltaTime,\n\t\t\t};\n\t\t}\n\n\t\t// 상태 업데이트\n\t\tmouseStateRef.current = {\n\t\t\tposition: normalizedPos,\n\t\t\tprevPosition: prevPos,\n\t\t\tvelocity,\n\t\t\tacceleration,\n\t\t\tisHovering: true,\n\t\t\tisDragging: state.isDragging,\n\t\t};\n\t\tprevVelocityRef.current = velocity;\n\t}, [toNormalized]);\n\n\t/**\n\t * 마우스 이동 핸들러\n\t */\n\tconst handleMouseMove = useCallback((e: MouseEvent) => {\n\t\tupdatePosition(e.clientX, e.clientY);\n\t}, [updatePosition]);\n\n\t/**\n\t * 마우스 진입\n\t */\n\tconst handleMouseEnter = useCallback(() => {\n\t\tmouseStateRef.current.isHovering = true;\n\t}, []);\n\n\t/**\n\t * 마우스 나감\n\t */\n\tconst handleMouseLeave = useCallback(() => {\n\t\tmouseStateRef.current = {\n\t\t\tposition: null,\n\t\t\tprevPosition: null,\n\t\t\tvelocity: { x: 0, y: 0 },\n\t\t\tacceleration: { x: 0, y: 0 },\n\t\t\tisHovering: false,\n\t\t\tisDragging: false,\n\t\t};\n\t\tprevVelocityRef.current = { x: 0, y: 0 };\n\t}, []);\n\n\t/**\n\t * 마우스 다운\n\t */\n\tconst handleMouseDown = useCallback(() => {\n\t\tmouseStateRef.current.isDragging = true;\n\t}, []);\n\n\t/**\n\t * 마우스 업\n\t */\n\tconst handleMouseUp = useCallback(() => {\n\t\tmouseStateRef.current.isDragging = false;\n\t}, []);\n\n\t/**\n\t * 터치 이동 핸들러\n\t */\n\tconst handleTouchMove = useCallback((e: TouchEvent) => {\n\t\te.preventDefault(); // 스크롤 방지\n\t\tif (e.touches.length > 0) {\n\t\t\tconst touch = e.touches[0];\n\t\t\tupdatePosition(touch.clientX, touch.clientY);\n\t\t}\n\t}, [updatePosition]);\n\n\t/**\n\t * 터치 시작 핸들러\n\t */\n\tconst handleTouchStart = useCallback((e: TouchEvent) => {\n\t\te.preventDefault(); // 스크롤 방지\n\t\tmouseStateRef.current.isDragging = true;\n\t\tmouseStateRef.current.isHovering = true;\n\t\tif (e.touches.length > 0) {\n\t\t\tconst touch = e.touches[0];\n\t\t\tupdatePosition(touch.clientX, touch.clientY);\n\t\t}\n\t}, [updatePosition]);\n\n\t/**\n\t * 터치 종료 핸들러\n\t */\n\tconst handleTouchEnd = useCallback(() => {\n\t\tmouseStateRef.current.isDragging = false;\n\t\tmouseStateRef.current.isHovering = false;\n\t\tmouseStateRef.current.position = null;\n\t\tmouseStateRef.current.prevPosition = null;\n\t\tmouseStateRef.current.velocity = { x: 0, y: 0 };\n\t\tmouseStateRef.current.acceleration = { x: 0, y: 0 };\n\t\tprevVelocityRef.current = { x: 0, y: 0 };\n\t}, []);\n\n\t/**\n\t * 이벤트 리스너 등록\n\t */\n\tuseEffect(() => {\n\t\tconst container = containerRef.current;\n\t\tif (!container) return;\n\n\t\t// 마우스 이벤트\n\t\tcontainer.addEventListener('mousemove', handleMouseMove);\n\t\tcontainer.addEventListener('mouseenter', handleMouseEnter);\n\t\tcontainer.addEventListener('mouseleave', handleMouseLeave);\n\t\tcontainer.addEventListener('mousedown', handleMouseDown);\n\t\twindow.addEventListener('mouseup', handleMouseUp);\n\n\t\t// 터치 이벤트 (passive: false로 스크롤 방지 가능)\n\t\tcontainer.addEventListener('touchmove', handleTouchMove, { passive: false });\n\t\tcontainer.addEventListener('touchstart', handleTouchStart, { passive: false });\n\t\tcontainer.addEventListener('touchend', handleTouchEnd);\n\t\tcontainer.addEventListener('touchcancel', handleTouchEnd);\n\n\t\treturn () => {\n\t\t\t// 마우스 이벤트 제거\n\t\t\tcontainer.removeEventListener('mousemove', handleMouseMove);\n\t\t\tcontainer.removeEventListener('mouseenter', handleMouseEnter);\n\t\t\tcontainer.removeEventListener('mouseleave', handleMouseLeave);\n\t\t\tcontainer.removeEventListener('mousedown', handleMouseDown);\n\t\t\twindow.removeEventListener('mouseup', handleMouseUp);\n\n\t\t\t// 터치 이벤트 제거\n\t\t\tcontainer.removeEventListener('touchmove', handleTouchMove);\n\t\t\tcontainer.removeEventListener('touchstart', handleTouchStart);\n\t\t\tcontainer.removeEventListener('touchend', handleTouchEnd);\n\t\t\tcontainer.removeEventListener('touchcancel', handleTouchEnd);\n\t\t};\n\t}, [containerRef, handleMouseMove, handleMouseEnter, handleMouseLeave, handleMouseDown, handleMouseUp, handleTouchMove, handleTouchStart, handleTouchEnd]);\n\n\t/**\n\t * 현재 마우스 상태 가져오기\n\t */\n\tconst getState = useCallback((): MouseState => {\n\t\treturn { ...mouseStateRef.current };\n\t}, []);\n\n\treturn {\n\t\tgetState,\n\t};\n};\n","import { Point } from '@/types';\r\nimport { SpringPhysicsConfig, SpringState } from '@/types/interaction';\r\n\r\n/**\r\n * 스프링 기반 물리 시뮬레이션 엔진\r\n * Hooke's Law와 감쇠를 적용한 스프링-댐퍼 시스템\r\n */\r\nexport class SpringPhysics {\r\n\tprivate config: SpringPhysicsConfig;\r\n\tprivate state: SpringState;\r\n\r\n\tconstructor(config: SpringPhysicsConfig) {\r\n\t\tthis.config = config;\r\n\t\tthis.state = {\r\n\t\t\tdisplacement: { x: 0, y: 0 },\r\n\t\t\tvelocity: { x: 0, y: 0 },\r\n\t\t\ttarget: { x: 0, y: 0 },\r\n\t\t};\r\n\t}\r\n\r\n\t/**\r\n\t * 물리 파라미터 업데이트\r\n\t */\r\n\tpublic setConfig(config: Partial<SpringPhysicsConfig>) {\r\n\t\tthis.config = { ...this.config, ...config };\r\n\t}\r\n\r\n\t/**\r\n\t * 목표 위치 설정 (마우스 속도 기반)\r\n\t */\r\n\tpublic setTarget(velocity: Point, velocityMultiplier: number = 1.0) {\r\n\t\t// 속도에 승수를 곱해서 목표 변위로 설정\r\n\t\tthis.state.target = {\r\n\t\t\tx: velocity.x * velocityMultiplier,\r\n\t\t\ty: velocity.y * velocityMultiplier,\r\n\t\t};\r\n\t}\r\n\r\n\t/**\r\n\t * 초기 속도 설정 (드래그 방향과 속도를 즉시 반영)\r\n\t * 드래그 방향으로 즉시 튕기는 효과\r\n\t */\r\n\tpublic setInitialVelocity(velocity: Point, multiplier: number = 1.0) {\r\n\t\t// 현재 속도를 즉시 변경\r\n\t\tthis.state.velocity = {\r\n\t\t\tx: velocity.x * multiplier,\r\n\t\t\ty: velocity.y * multiplier,\r\n\t\t};\r\n\t\t// 목표는 0 (평형 상태로 돌아가도록)\r\n\t\tthis.state.target = { x: 0, y: 0 };\r\n\t}\r\n\r\n\t/**\r\n\t * 스프링 물리 업데이트 (Hooke's Law + Damping)\r\n\t * F = -k * x - c * v\r\n\t * a = F / m\r\n\t * v += a * dt\r\n\t * x += v * dt\r\n\t */\r\n\tpublic update(deltaTime: number): Point {\r\n\t\tconst { stiffness, damping, mass } = this.config;\r\n\t\tconst { displacement, velocity, target } = this.state;\r\n\r\n\t\t// 평형 위치로부터의 변위 (target은 마우스 속도에서 계산된 목표)\r\n\t\tconst dx = displacement.x - target.x;\r\n\t\tconst dy = displacement.y - target.y;\r\n\r\n\t\t// 스프링 힘: F = -k * x (복원력)\r\n\t\tconst springForceX = -stiffness * dx;\r\n\t\tconst springForceY = -stiffness * dy;\r\n\r\n\t\t// 감쇠 힘: F = -c * v (마찰력)\r\n\t\tconst dampingForceX = -damping * velocity.x;\r\n\t\tconst dampingForceY = -damping * velocity.y;\r\n\r\n\t\t// 총 힘\r\n\t\tconst totalForceX = springForceX + dampingForceX;\r\n\t\tconst totalForceY = springForceY + dampingForceY;\r\n\r\n\t\t// 가속도: a = F / m\r\n\t\tconst accelerationX = totalForceX / mass;\r\n\t\tconst accelerationY = totalForceY / mass;\r\n\r\n\t\t// 속도 업데이트: v += a * dt\r\n\t\tconst newVelocityX = velocity.x + accelerationX * deltaTime;\r\n\t\tconst newVelocityY = velocity.y + accelerationY * deltaTime;\r\n\r\n\t\t// 위치 업데이트: x += v * dt\r\n\t\tconst newDisplacementX = displacement.x + newVelocityX * deltaTime;\r\n\t\tconst newDisplacementY = displacement.y + newVelocityY * deltaTime;\r\n\r\n\t\t// 상태 저장\r\n\t\tthis.state = {\r\n\t\t\tdisplacement: { x: newDisplacementX, y: newDisplacementY },\r\n\t\t\tvelocity: { x: newVelocityX, y: newVelocityY },\r\n\t\t\ttarget,\r\n\t\t};\r\n\r\n\t\t// 매우 작은 움직임은 0으로 처리 (정지 판정)\r\n\t\tconst isNearlyZero = (val: number) => Math.abs(val) < 0.0001;\r\n\t\tif (isNearlyZero(this.state.displacement.x) && isNearlyZero(this.state.displacement.y) &&\r\n\t\t\tisNearlyZero(this.state.velocity.x) && isNearlyZero(this.state.velocity.y)) {\r\n\t\t\tthis.reset();\r\n\t\t}\r\n\r\n\t\treturn this.state.displacement;\r\n\t}\r\n\r\n\t/**\r\n\t * 즉시 충격 적용 (마우스 가속도 기반)\r\n\t */\r\n\tpublic applyImpulse(acceleration: Point, multiplier: number = 1.0) {\r\n\t\t// 가속도를 속도로 변환하여 즉시 적용\r\n\t\tthis.state.velocity.x += acceleration.x * multiplier;\r\n\t\tthis.state.velocity.y += acceleration.y * multiplier;\r\n\t}\r\n\r\n\t/**\r\n\t * 현재 변위 가져오기\r\n\t */\r\n\tpublic getDisplacement(): Point {\r\n\t\treturn { ...this.state.displacement };\r\n\t}\r\n\r\n\t/**\r\n\t * 현재 속도 가져오기\r\n\t */\r\n\tpublic getVelocity(): Point {\r\n\t\treturn { ...this.state.velocity };\r\n\t}\r\n\r\n\t/**\r\n\t * 상태 리셋\r\n\t */\r\n\tpublic reset() {\r\n\t\tthis.state = {\r\n\t\t\tdisplacement: { x: 0, y: 0 },\r\n\t\t\tvelocity: { x: 0, y: 0 },\r\n\t\t\ttarget: { x: 0, y: 0 },\r\n\t\t};\r\n\t}\r\n\r\n\t/**\r\n\t * 마우스가 멈췄을 때 목표를 0으로 설정 (평형 상태로 복귀)\r\n\t */\r\n\tpublic returnToEquilibrium() {\r\n\t\tthis.state.target = { x: 0, y: 0 };\r\n\t}\r\n}\r\n","/**\n * 셰이더 관련 설정\n */\nexport const SHADER_CONFIG = {\n /** 최대 영역 개수 */\n MAX_AREAS: 8,\n /** 최대 포인트 개수 (8영역 × 4포인트) */\n MAX_POINTS: 32,\n /** 최대 드래그 벡터 개수 */\n MAX_DRAG_VECTORS: 8,\n /** 최대 강도 배열 크기 */\n MAX_STRENGTHS: 8,\n /** 최대 렌즈 효과 배열 크기 */\n MAX_LENS_EFFECTS: 8,\n} as const;\n\n/**\n * 애니메이션 관련 설정\n */\nexport const ANIMATION_CONFIG = {\n /** 목표 FPS */\n TARGET_FPS: 60,\n /** 델타 타임 (약 16.67ms) */\n DELTA_TIME: 1 / 60,\n} as const;\n\n/**\n * 기본 영역 설정값\n */\nexport const DEFAULT_AREA = {\n /** 기본 왜곡 강도 */\n DISTORTION_STRENGTH: 0.5,\n /** 기본 애니메이션 지속 시간 (초) */\n DURATION: 2.0,\n /** 기본 이징 함수 */\n EASING: 'easeInOut' as const,\n /** 기본 벡터 A */\n VECTOR_A: { x: 0.1, y: 0.1 },\n /** 기본 벡터 B */\n VECTOR_B: { x: -0.1, y: -0.1 },\n /** 기본 렌즈 효과 강도 */\n LENS_STRENGTH: 0,\n /** 기본 스텝 양자화 단계 수 (0=없음) */\n SNAP_STEPS: 0,\n} as const;","import React, {useRef, useEffect, useState, useCallback, useMemo} from 'react';\r\nimport {DistortionArea, Point} from '@/types';\r\nimport type {SpriteEffectArea} from '@/types/spriteEffect';\r\nimport {ImageDistortion} from '@/components/ImageDistortion';\r\nimport {EditorCanvasStyle} from '../types';\r\nimport {DEFAULT_EDITOR_CANVAS_STYLE} from '@/editor';\r\n\r\nexport interface EditorCanvasProps {\r\n\tareas: DistortionArea[];\r\n\tselectedAreaId: string | null;\r\n\timageSrc: string;\r\n\twidth: number;\r\n\theight: number;\r\n\tonUpdatePoint: (areaId: string, pointIndex: number, point: Point) => void;\r\n\tonUpdateArea: (areaId: string, updates: Partial<DistortionArea>) => void;\r\n\tdraggingPointIndex: number | null;\r\n\tonStartDragging: (pointIndex: number) => void;\r\n\tonStopDragging: () => void;\r\n\t/** 에디터 캔버스 스타일 커스터마이징 */\r\n\tstyle?: EditorCanvasStyle;\r\n\t/** 에디터 UI 표시 여부 (기본값: true) */\r\n\tshowEditor?: boolean;\r\n\t/** 영역 선택 콜백 (비선택 영역 클릭 시) */\r\n\tonSelectArea?: (areaId: string) => void;\r\n\t/** 독립 스프라이트 이펙트 영역 */\r\n\tspriteEffectAreas?: SpriteEffectArea[];\r\n\t/** 스프라이트 이펙트 영역 업데이트 콜백 */\r\n\tonUpdateSpriteEffectArea?: (areaId: string, updates: Partial<SpriteEffectArea>) => void;\r\n}\r\n\r\nexport const EditorCanvas: React.FC<EditorCanvasProps> = ({\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t areas,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t selectedAreaId,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t imageSrc,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t width,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t height,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t onUpdatePoint,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t onUpdateArea,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t draggingPointIndex,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t onStartDragging,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t onStopDragging,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t style: customStyle,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t showEditor = true,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t onSelectArea,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t spriteEffectAreas = [],\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t onUpdateSpriteEffectArea,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t }) => {\r\n\tconst containerRef = useRef<HTMLDivElement>(null);\r\n\tconst [canvasSize, setCanvasSize] = useState({width: 0, height: 0});\r\n\tconst [isDraggingArea, setIsDraggingArea] = useState(false);\r\n\tconst [dragStartPos, setDragStartPos] = useState<Point | null>(null);\r\n\tconst [draggingSpriteAreaId, setDraggingSpriteAreaId] = useState<string | null>(null);\r\n\r\n\t// 스타일 병합 (커스텀 스타일 우선)\r\n\tconst editorStyle = useMemo(() => ({\r\n\t\t...DEFAULT_EDITOR_CANVAS_STYLE,\r\n\t\t...customStyle,\r\n\t\tcircleLevels: customStyle?.circleLevels || DEFAULT_EDITOR_CANVAS_STYLE.circleLevels,\r\n\t\tcenterPoint: {\r\n\t\t\t...DEFAULT_EDITOR_CANVAS_STYLE.centerPoint,\r\n\t\t\t...customStyle?.centerPoint,\r\n\t\t},\r\n\t\tpointHandle: {\r\n\t\t\t...DEFAULT_EDITOR_CANVAS_STYLE.pointHandle,\r\n\t\t\t...customStyle?.pointHandle,\r\n\t\t},\r\n\t\tareaOutline: {\r\n\t\t\t...DEFAULT_EDITOR_CANVAS_STYLE.areaOutline,\r\n\t\t\t...customStyle?.areaOutline,\r\n\t\t},\r\n\t}), [customStyle]);\r\n\r\n\t// 컨테이너 크기 측정 (ResizeObserver 사용)\r\n\tuseEffect(() => {\r\n\t\tif (!containerRef.current) return;\r\n\r\n\t\tconst updateSize = () => {\r\n\t\t\tif (!containerRef.current) return;\r\n\t\t\tconst rect = containerRef.current.getBoundingClientRect();\r\n\t\t\tsetCanvasSize({width: rect.width, height: rect.height});\r\n\t\t};\r\n\r\n\t\t// 초기 크기 설정\r\n\t\tupdateSize();\r\n\r\n\t\t// ResizeObserver로 크기 변경 감지\r\n\t\tconst resizeObserver = new ResizeObserver(updateSize);\r\n\t\tresizeObserver.observe(containerRef.current);\r\n\r\n\t\treturn () => {\r\n\t\t\tresizeObserver.disconnect();\r\n\t\t};\r\n\t}, []);\r\n\r\n\t// 선택된 영역 찾기\r\n\tconst selectedArea = areas.find((a) => a.id === selectedAreaId);\r\n\r\n\t// 점이 사각형 내부에 있는지 확인 (Point-in-Polygon test)\r\n\tconst isPointInPolygon = useCallback((point: Point, polygon: Point[]): boolean => {\r\n\t\tlet inside = false;\r\n\t\tfor (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {\r\n\t\t\tconst xi = polygon[i].x, yi = polygon[i].y;\r\n\t\t\tconst xj = polygon[j].x, yj = polygon[j].y;\r\n\r\n\t\t\tconst intersect = ((yi > point.y) !== (yj > point.y))\r\n\t\t\t\t&& (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);\r\n\t\t\tif (intersect) inside = !inside;\r\n\t\t}\r\n\t\treturn inside;\r\n\t}, []);\r\n\r\n\t// 포인트 핸들 클릭/터치 핸들러\r\n\tconst handlePointDown = useCallback(\r\n\t\t(pointIndex: number) => (e: React.MouseEvent | React.TouchEvent) => {\r\n\t\t\te.preventDefault();\r\n\t\t\te.stopPropagation();\r\n\t\t\tonStartDragging(pointIndex);\r\n\t\t},\r\n\t\t[onStartDragging]\r\n\t);\r\n\r\n\t// 캔버스 다운 (마우스/터치 공통)\r\n\tconst handleCanvasDown = useCallback(\r\n\t\t(e: React.MouseEvent | React.TouchEvent) => {\r\n\t\t\t// 에디터가 숨겨진 상태면 동작하지 않음\r\n\t\t\tif (!showEditor || !containerRef.current) return;\r\n\r\n\t\t\tconst rect = containerRef.current.getBoundingClientRect();\r\n\r\n\t\t\t// 마우스 또는 터치 좌표 추출\r\n\t\t\tlet clientX: number, clientY: number;\r\n\t\t\tif ('touches' in e) {\r\n\t\t\t\tif (e.touches.length === 0) return;\r\n\t\t\t\tclientX = e.touches[0].clientX;\r\n\t\t\t\tclientY = e.touches[0].clientY;\r\n\t\t\t} else {\r\n\t\t\t\tclientX = e.clientX;\r\n\t\t\t\tclientY = e.clientY;\r\n\t\t\t}\r\n\r\n\t\t\tconst x = (clientX - rect.left) / rect.width;\r\n\t\t\tconst y = (clientY - rect.top) / rect.height;\r\n\t\t\tconst clickPoint = { x, y };\r\n\r\n\t\t\t// 스프라이트 이펙트 영역 클릭 확인 (우선 처리)\r\n\t\t\tif (onUpdateSpriteEffectArea) {\r\n\t\t\t\tfor (let i = spriteEffectAreas.length - 1; i >= 0; i--) {\r\n\t\t\t\t\tconst sa = spriteEffectAreas[i];\r\n\t\t\t\t\tconst dx = clickPoint.x - sa.position.x;\r\n\t\t\t\t\tconst dy = clickPoint.y - sa.position.y;\r\n\t\t\t\t\tconst radius = sa.radius ?? 0.1;\r\n\t\t\t\t\tif (dx * dx + dy * dy <= radius * radius) {\r\n\t\t\t\t\t\tsetDraggingSpriteAreaId(sa.id);\r\n\t\t\t\t\t\tsetDragStartPos(clickPoint);\r\n\t\t\t\t\t\te.preventDefault();\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// 선택된 영역 내부를 클릭했는지 확인 (드래그 시작)\r\n\t\t\tif (selectedArea && isPointInPolygon(clickPoint, selectedArea.basePoints)) {\r\n\t\t\t\tsetIsDraggingArea(true);\r\n\t\t\t\tsetDragStartPos(clickPoint);\r\n\t\t\t\te.preventDefault();\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// 비선택 영역 클릭 시 해당 영역 선택\r\n\t\t\tif (onSelectArea) {\r\n\t\t\t\t// 역순으로 검사 (위에 그려진 영역 우선)\r\n\t\t\t\tfor (let i = areas.length - 1; i >= 0; i--) {\r\n\t\t\t\t\tconst area = areas[i];\r\n\t\t\t\t\tif (area.id !== selectedAreaId && isPointInPolygon(clickPoint, area.basePoints)) {\r\n\t\t\t\t\t\tonSelectArea(area.id);\r\n\t\t\t\t\t\te.preventDefault();\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t},\r\n\t\t[showEditor, selectedArea, selectedAreaId, areas, isPointInPolygon, onSelectArea, spriteEffectAreas, onUpdateSpriteEffectArea]\r\n\t);\r\n\r\n\t// 이동 (마우스/터치 공통)\r\n\tconst handleMove = useCallback(\r\n\t\t(e: React.MouseEvent | React.TouchEvent) => {\r\n\t\t\t// 에디터가 숨겨진 상태면 동작하지 않음\r\n\t\t\tif (!showEditor || !containerRef.current) return;\r\n\r\n\t\t\t// 터치 이벤트면 스크롤 방지\r\n\t\t\tif ('touches' in e && (draggingPointIndex !== null || isDraggingArea || draggingSpriteAreaId)) {\r\n\t\t\t\te.preventDefault();\r\n\t\t\t}\r\n\r\n\t\t\tconst rect = containerRef.current.getBoundingClientRect();\r\n\r\n\t\t\t// 마우스 또는 터치 좌표 추출\r\n\t\t\tlet clientX: number, clientY: number;\r\n\t\t\tif ('touches' in e) {\r\n\t\t\t\tif (e.touches.length === 0) return;\r\n\t\t\t\tclientX = e.touches[0].clientX;\r\n\t\t\t\tclientY = e.touches[0].clientY;\r\n\t\t\t} else {\r\n\t\t\t\tclientX = e.clientX;\r\n\t\t\t\tclientY = e.clientY;\r\n\t\t\t}\r\n\r\n\t\t\tconst x = (clientX - rect.left) / rect.width;\r\n\t\t\tconst y = (clientY - rect.top) / rect.height;\r\n\r\n\t\t\t// 스프라이트 이펙트 영역 드래그 중\r\n\t\t\tif (draggingSpriteAreaId && dragStartPos && onUpdateSpriteEffectArea) {\r\n\t\t\t\tconst sa = spriteEffectAreas.find(a => a.id === draggingSpriteAreaId);\r\n\t\t\t\tif (sa) {\r\n\t\t\t\t\tconst deltaX = x - dragStartPos.x;\r\n\t\t\t\t\tconst deltaY = y - dragStartPos.y;\r\n\t\t\t\t\tonUpdateSpriteEffectArea(sa.id, {\r\n\t\t\t\t\t\tposition: {\r\n\t\t\t\t\t\t\tx: Math.max(0, Math.min(1, sa.position.x + deltaX)),\r\n\t\t\t\t\t\t\ty: Math.max(0, Math.min(1, sa.position.y + deltaY)),\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t});\r\n\t\t\t\t\tsetDragStartPos({x, y});\r\n\t\t\t\t}\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!selectedArea) return;\r\n\r\n\t\t\t// 포인트 드래그 중\r\n\t\t\tif (draggingPointIndex !== null) {\r\n\t\t\t\tconst clampedX = Math.max(0, Math.min(1, x));\r\n\t\t\t\tconst clampedY = Math.max(0, Math.min(1, y));\r\n\t\t\t\tonUpdatePoint(selectedArea.id, draggingPointIndex, {x: clampedX, y: clampedY});\r\n\t\t\t}\r\n\t\t\t// 사각형 전체 드래그 중\r\n\t\t\telse if (isDraggingArea && dragStartPos) {\r\n\t\t\t\tconst deltaX = x - dragStartPos.x;\r\n\t\t\t\tconst deltaY = y - dragStartPos.y;\r\n\r\n\t\t\t\t// 모든 포인트를 delta만큼 이동\r\n\t\t\t\tconst newPoints = selectedArea.basePoints.map((point) => ({\r\n\t\t\t\t\tx: Math.max(0, Math.min(1, point.x + deltaX)),\r\n\t\t\t\t\ty: Math.max(0, Math.min(1, point.y + deltaY)),\r\n\t\t\t\t})) as [Point, Point, Point, Point];\r\n\r\n\t\t\t\tonUpdateArea(selectedArea.id, { basePoints: newPoints });\r\n\t\t\t\tsetDragStartPos({ x, y }); // 다음 프레임을 위해 시작 위치 업데이트\r\n\t\t\t}\r\n\t\t},\r\n\t\t[showEditor, draggingPointIndex, isDraggingArea, draggingSpriteAreaId, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea, spriteEffectAreas, onUpdateSpriteEffectArea]\r\n\t);\r\n\r\n\t// 업 (마우스/터치 공통)\r\n\tconst handleUp = useCallback(() => {\r\n\t\tif (draggingPointIndex !== null) {\r\n\t\t\tonStopDragging();\r\n\t\t}\r\n\t\tif (isDraggingArea) {\r\n\t\t\tsetIsDraggingArea(false);\r\n\t\t\tsetDragStartPos(null);\r\n\t\t}\r\n\t\tif (draggingSpriteAreaId) {\r\n\t\t\tsetDraggingSpriteAreaId(null);\r\n\t\t\tsetDragStartPos(null);\r\n\t\t}\r\n\t}, [draggingPointIndex, isDraggingArea, draggingSpriteAreaId, onStopDragging]);\r\n\r\n\t// 전역 업 이벤트 (마우스/터치)\r\n\tuseEffect(() => {\r\n\t\tif (draggingPointIndex !== null || isDraggingArea || draggingSpriteAreaId) {\r\n\t\t\twindow.addEventListener('mouseup', handleUp);\r\n\t\t\twindow.addEventListener('touchend', handleUp);\r\n\t\t\twindow.addEventListener('touchcancel', handleUp);\r\n\t\t\treturn () => {\r\n\t\t\t\twindow.removeEventListener('mouseup', handleUp);\r\n\t\t\t\twindow.removeEventListener('touchend', handleUp);\r\n\t\t\t\twindow.removeEventListener('touchcancel', handleUp);\r\n\t\t\t};\r\n\t\t}\r\n\t}, [draggingPointIndex, isDraggingArea, draggingSpriteAreaId, handleUp]);\r\n\r\n\t// UV 좌표를 픽셀 좌표로 변환 (셰이더와 동일한 bilinear interpolation)\r\n\tconst uvToPixel = (\r\n\t\tu: number,\r\n\t\tv: number,\r\n\t\tpoints: [Point, Point, Point, Point],\r\n\t\tcanvasWidth: number,\r\n\t\tcanvasHeight: number\r\n\t): { x: number; y: number } => {\r\n\t\t// p0=좌상, p1=우상, p2=우하, p3=좌하\r\n\t\tconst [p0, p1, p2, p3] = points;\r\n\r\n\t\t// 셰이더 computeUV와 동일한 순서로 bilinear interpolation\r\n\t\t// left = mix(p0, p1, u) -> 상단 가장자리\r\n\t\t// right = mix(p3, p2, u) -> 하단 가장자리\r\n\t\t// position = mix(left, right, v)\r\n\t\tconst leftX = p0.x * (1 - u) + p1.x * u;\r\n\t\tconst leftY = p0.y * (1 - u) + p1.y * u;\r\n\r\n\t\tconst rightX = p3.x * (1 - u) + p2.x * u;\r\n\t\tconst rightY = p3.y * (1 - u) + p2.y * u;\r\n\r\n\t\tconst posX = leftX * (1 - v) + rightX * v;\r\n\t\tconst posY = leftY * (1 - v) + rightY * v;\r\n\r\n\t\treturn {\r\n\t\t\tx: posX * canvasWidth,\r\n\t\t\ty: posY * canvasHeight,\r\n\t\t};\r\n\t};\r\n\r\n\t// UV 좌표계의 원을 정확히 그리기 (찌그러진 원 형태)\r\n\tconst drawDistortionCircle = useCallback((\r\n\t\tctx: CanvasRenderingContext2D,\r\n\t\tpoints: [Point, Point, Point, Point],\r\n\t\tcanvasWidth: number,\r\n\t\tcanvasHeight: number\r\n\t) => {\r\n\t\tconst segments = 128; // 원을 128개 세그먼트로 촘촘히 분할\r\n\t\tconst centerU = 0.5;\r\n\t\tconst centerV = 0.5;\r\n\r\n\t\tconst circleLevels = editorStyle.circleLevels || [];\r\n\r\n\t\t// 원 레벨별로 그리기 (외부 -> 내부 순)\r\n\t\tcircleLevels.forEach((level, index) => {\r\n\t\t\tconst levelPoints: { x: number; y: number }[] = [];\r\n\t\t\tfor (let i = 0; i <= segments; i++) {\r\n\t\t\t\tconst theta = (i / segments) * 2 * Math.PI;\r\n\t\t\t\tconst u = centerU - level.radius * Math.sin(theta);\r\n\t\t\t\tconst v = centerV + level.radius * Math.cos(theta);\r\n\t\t\t\tconst pixelPos = uvToPixel(u, v, points, canvasWidth, canvasHeight);\r\n\t\t\t\tlevelPoints.push(pixelPos);\r\n\t\t\t}\r\n\r\n\t\t\tctx.beginPath();\r\n\t\t\tctx.moveTo(levelPoints[0].x, levelPoints[0].y);\r\n\t\t\tfor (let i = 1; i < levelPoints.length; i++) {\r\n\t\t\t\tctx.lineTo(levelPoints[i].x, levelPoints[i].y);\r\n\t\t\t}\r\n\t\t\tctx.closePath();\r\n\r\n\t\t\t// 원 테두리\r\n\t\t\tconst baseColor = level.color || 'rgba(255, 200, 0, 1)';\r\n\t\t\t// baseColor에서 RGB 추출하고 opacity 적용\r\n\t\t\tconst colorWithOpacity = baseColor.replace(/rgba?\\(([^)]+)\\)/, (_, rgb) => {\r\n\t\t\t\tconst parts = rgb.split(',').map((p: string) => p.trim());\r\n\t\t\t\treturn `rgba(${parts[0]}, ${parts[1]}, ${parts[2]}, ${level.opacity})`;\r\n\t\t\t});\r\n\t\t\tctx.strokeStyle = colorWithOpacity;\r\n\t\t\tctx.lineWidth = level.lineWidth;\r\n\t\t\tif (level.dashPattern) {\r\n\t\t\t\tctx.setLineDash(level.dashPattern);\r\n\t\t\t}\r\n\t\t\tctx.stroke();\r\n\t\t\tctx.setLineDash([]);\r\n\r\n\t\t\t// 가장 외부 원만 내부 채우기\r\n\t\t\tif (index === 0 && editorStyle.circleFillColor) {\r\n\t\t\t\tctx.fillStyle = editorStyle.circleFillColor;\r\n\t\t\t\tctx.fill();\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\t// 중심점 표시\r\n\t\tconst centerPointStyle = editorStyle.centerPoint || {};\r\n\t\tconst centerPixel = uvToPixel(centerU, centerV, points, canvasWidth, canvasHeight);\r\n\t\tctx.beginPath();\r\n\t\tctx.arc(centerPixel.x, centerPixel.y, centerPointStyle.radius || 5, 0, 2 * Math.PI);\r\n\t\tif (centerPointStyle.fillColor) {\r\n\t\t\tctx.fillStyle = centerPointStyle.fillColor;\r\n\t\t\tctx.fill();\r\n\t\t}\r\n\t\tif (centerPointStyle.strokeColor) {\r\n\t\t\tctx.strokeStyle = centerPointStyle.strokeColor;\r\n\t\t\tctx.lineWidth = centerPointStyle.strokeWidth || 2;\r\n\t\t\tctx.stroke();\r\n\t\t}\r\n\t}, [editorStyle]);\r\n\r\n\t// 커서 스타일 결정\r\n\tconst getCursorStyle = () => {\r\n\t\tif (draggingPointIndex !== null) return 'grabbing';\r\n\t\tif (isDraggingArea) return 'grabbing';\r\n\t\tif (draggingSpriteAreaId) return 'grabbing';\r\n\t\treturn 'default';\r\n\t};\r\n\r\n\treturn (\r\n\t\t<div\r\n\t\t\tref={containerRef}\r\n\t\t\tclassName=\"editor-canvas\"\r\n\t\t\tstyle={{\r\n\t\t\t\twidth: '100%',\r\n\t\t\t\theight: '100%',\r\n\t\t\t\tposition: 'relative',\r\n\t\t\t\tcursor: showEditor ? getCursorStyle() : 'default',\r\n\t\t\t\tpointerEvents: showEditor ? 'auto' : 'none',\r\n\t\t\t\ttouchAction: 'none', // 터치 시 모든 브라우저 동작 비활성화 (스크롤, 줌 등)\r\n\t\t\t}}\r\n\t\t\tonMouseDown={showEditor ? handleCanvasDown : undefined}\r\n\t\t\tonMouseMove={showEditor ? handleMove : undefined}\r\n\t\t\tonTouchStart={showEditor ? handleCanvasDown : undefined}\r\n\t\t\tonTouchMove={showEditor ? handleMove : undefined}\r\n\t\t>\r\n\t\t\t{/* ImageDistortion 컴포넌트 */}\r\n\t\t\t<ImageDistortion imageSrc={imageSrc} areas={areas} spriteEffectAreas={spriteEffectAreas}/>\r\n\r\n\t\t\t{/* 오버레이 SVG - 에디터 모드일 때만 표시 */}\r\n\t\t\t{showEditor && (\r\n\t\t\t\t<svg\r\n\t\t\t\t\tstyle={{\r\n\t\t\t\t\t\tposition: 'absolute',\r\n\t\t\t\t\t\ttop: 0,\r\n\t\t\t\t\t\tleft: 0,\r\n\t\t\t\t\t\twidth: '100%',\r\n\t\t\t\t\t\theight: '100%',\r\n\t\t\t\t\t\tpointerEvents: 'none',\r\n\t\t\t\t\t}}\r\n\t\t\t\t>\r\n\t\t\t\t\t{/* 모든 영역의 사각형 표시 */}\r\n\t\t\t\t\t{areas.map((area) => {\r\n\t\t\t\t\tconst isSelected = area.id === selectedAreaId;\r\n\t\t\t\t\tconst points = area.basePoints;\r\n\t\t\t\t\tconst outlineStyle = editorStyle.areaOutline || {};\r\n\t\t\t\t\treturn (\r\n\t\t\t\t\t\t<g key={area.id}>\r\n\t\t\t\t\t\t\t{/* 사각형 배경 및 경계선 */}\r\n\t\t\t\t\t\t\t<polygon\r\n\t\t\t\t\t\t\t\tpoints={points\r\n\t\t\t\t\t\t\t\t\t.map((p) => `${p.x * canvasSize.width},${p.y * canvasSize.height}`)\r\n\t\t\t\t\t\t\t\t\t.join(' ')}\r\n\t\t\t\t\t\t\t\tfill={isSelected ? (outlineStyle.selectedFillColor || 'rgba(0, 170, 255, 0.08)') : (outlineStyle.unselectedFillColor || 'rgba(136, 136, 136, 0.03)')}\r\n\t\t\t\t\t\t\t\tstroke={isSelected ? (outlineStyle.selectedColor || '#00aaff') : (outlineStyle.unselectedColor || '#888')}\r\n\t\t\t\t\t\t\t\tstrokeWidth={isSelected ? (outlineStyle.selectedWidth || 2) : (outlineStyle.unselectedWidth || 1)}\r\n\t\t\t\t\t\t\t\tstrokeDasharray={isSelected ? '0' : (outlineStyle.unselectedDashPattern?.join(',') || '5,5')}\r\n\t\t\t\t\t\t\t\topacity={isSelected ? 1 : 0.5}\r\n\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t</g>\r\n\t\t\t\t\t);\r\n\t\t\t\t\t})}\r\n\t\t\t\t</svg>\r\n\t\t\t)}\r\n\r\n\t\t\t{/* 선택된 영역의 타원 가이드 (Canvas로 그리기) - 에디터 모드일 때만 표시 */}\r\n\t\t\t{showEditor && selectedArea && canvasSize.width > 0 && (\r\n\t\t\t\t<canvas\r\n\t\t\t\t\tstyle={{\r\n\t\t\t\t\t\tposition: 'absolute',\r\n\t\t\t\t\t\ttop: 0,\r\n\t\t\t\t\t\tleft: 0,\r\n\t\t\t\t\t\twidth: '100%',\r\n\t\t\t\t\t\theight: '100%',\r\n\t\t\t\t\t\tpointerEvents: 'none',\r\n\t\t\t\t\t}}\r\n\t\t\t\t\twidth={canvasSize.width}\r\n\t\t\t\t\theight={canvasSize.height}\r\n\t\t\t\t\tref={(canvas) => {\r\n\t\t\t\t\t\tif (canvas) {\r\n\t\t\t\t\t\t\tconst ctx = canvas.getContext('2d');\r\n\t\t\t\t\t\t\tif (ctx) {\r\n\t\t\t\t\t\t\t\tctx.clearRect(0, 0, canvasSize.width, canvasSize.height);\r\n\t\t\t\t\t\t\t\tdrawDistortionCircle(ctx, selectedArea.basePoints, canvasSize.width, canvasSize.height);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}}\r\n\t\t\t\t/>\r\n\t\t\t)}\r\n\r\n\t\t\t{/* 선택된 영역의 포인트 핸들 - 에디터 모드일 때만 표시 */}\r\n\t\t\t{showEditor && selectedArea &&\r\n\t\t\t\tselectedArea.basePoints.map((point, index) => {\r\n\t\t\t\t\tconst handleStyle = editorStyle.pointHandle || {};\r\n\t\t\t\t\treturn (\r\n\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\tkey={index}\r\n\t\t\t\t\t\t\tclassName={`point-handle ${draggingPointIndex === index ? 'dragging' : ''}`}\r\n\t\t\t\t\t\t\tstyle={{\r\n\t\t\t\t\t\t\t\tposition: 'absolute',\r\n\t\t\t\t\t\t\t\tleft: `${point.x * 100}%`,\r\n\t\t\t\t\t\t\t\ttop: `${point.y * 100}%`,\r\n\t\t\t\t\t\t\t\ttransform: 'translate(-50%, -50%)',\r\n\t\t\t\t\t\t\t\twidth: handleStyle.size || 16,\r\n\t\t\t\t\t\t\t\theight: handleStyle.size || 16,\r\n\t\t\t\t\t\t\t\tborderRadius: '50%',\r\n\t\t\t\t\t\t\t\tbackgroundColor: handleStyle.fillColor || '#00aaff',\r\n\t\t\t\t\t\t\t\tborder: `${handleStyle.strokeWidth || 2}px solid ${handleStyle.strokeColor || 'white'}`,\r\n\t\t\t\t\t\t\t\tcursor: 'grab',\r\n\t\t\t\t\t\t\t\tpointerEvents: 'auto',\r\n\t\t\t\t\t\t\t\tboxShadow: '0 2px 4px rgba(0,0,0,0.3)',\r\n\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\tonMouseDown={handlePointDown(index)}\r\n\t\t\t\t\t\t\tonTouchStart={handlePointDown(index)}\r\n\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\t\tstyle={{\r\n\t\t\t\t\t\t\t\t\tposition: 'absolute',\r\n\t\t\t\t\t\t\t\t\ttop: -24,\r\n\t\t\t\t\t\t\t\t\tleft: '50%',\r\n\t\t\t\t\t\t\t\t\ttransform: 'translateX(-50%)',\r\n\t\t\t\t\t\t\t\t\tfontSize: handleStyle.labelFontSize || 11,\r\n\t\t\t\t\t\t\t\t\tcolor: handleStyle.labelColor || '#00aaff',\r\n\t\t\t\t\t\t\t\t\tfontWeight: 'bold',\r\n\t\t\t\t\t\t\t\t\ttextShadow: '1px 1px 2px rgba(0,0,0,0.8)',\r\n\t\t\t\t\t\t\t\t\twhiteSpace: 'nowrap',\r\n\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\tP{index + 1}\r\n\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t);\r\n\t\t\t\t})}\r\n\r\n\t\t\t{/* 스프라이트 이펙트 영역 표시 */}\r\n\t\t\t{showEditor && spriteEffectAreas.map((sa) => {\r\n\t\t\t\tconst cx = sa.position.x * 100;\r\n\t\t\t\tconst cy = sa.position.y * 100;\r\n\t\t\t\tconst isDragging = draggingSpriteAreaId === sa.id;\r\n\t\t\t\t// 반경을 %로 변환 (가로 기준, 종횡비 보정은 SVG에서)\r\n\t\t\t\tconst radiusPx = (sa.radius ?? 0.1) * canvasSize.width;\r\n\r\n\t\t\t\treturn (\r\n\t\t\t\t\t<div\r\n\t\t\t\t\t\tkey={`sprite-${sa.id}`}\r\n\t\t\t\t\t\tstyle={{\r\n\t\t\t\t\t\t\tposition: 'absolute',\r\n\t\t\t\t\t\t\tleft: `${cx}%`,\r\n\t\t\t\t\t\t\ttop: `${cy}%`,\r\n\t\t\t\t\t\t\ttransform: 'translate(-50%, -50%)',\r\n\t\t\t\t\t\t\tpointerEvents: 'none',\r\n\t\t\t\t\t\t}}\r\n\t\t\t\t\t>\r\n\t\t\t\t\t\t{/* 반경 원 */}\r\n\t\t\t\t\t\t<svg\r\n\t\t\t\t\t\t\twidth={radiusPx * 2}\r\n\t\t\t\t\t\t\theight={radiusPx * 2}\r\n\t\t\t\t\t\t\tstyle={{\r\n\t\t\t\t\t\t\t\tposition: 'absolute',\r\n\t\t\t\t\t\t\t\tleft: -radiusPx,\r\n\t\t\t\t\t\t\t\ttop: -radiusPx,\r\n\t\t\t\t\t\t\t\tpointerEvents: 'none',\r\n\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t<circle\r\n\t\t\t\t\t\t\t\tcx={radiusPx}\r\n\t\t\t\t\t\t\t\tcy={radiusPx}\r\n\t\t\t\t\t\t\t\tr={radiusPx}\r\n\t\t\t\t\t\t\t\tfill={isDragging ? 'rgba(255, 170, 0, 0.15)' : 'rgba(255, 170, 0, 0.08)'}\r\n\t\t\t\t\t\t\t\tstroke={isDragging ? '#ffaa00' : 'rgba(255, 170, 0, 0.6)'}\r\n\t\t\t\t\t\t\t\tstrokeWidth={isDragging ? 2 : 1.5}\r\n\t\t\t\t\t\t\t\tstrokeDasharray={isDragging ? '0' : '4,3'}\r\n\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t</svg>\r\n\t\t\t\t\t\t{/* 중심 핸들 */}\r\n\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\tstyle={{\r\n\t\t\t\t\t\t\t\tposition: 'absolute',\r\n\t\t\t\t\t\t\t\tleft: -8,\r\n\t\t\t\t\t\t\t\ttop: -8,\r\n\t\t\t\t\t\t\t\twidth: 16,\r\n\t\t\t\t\t\t\t\theight: 16,\r\n\t\t\t\t\t\t\t\tborderRadius: '50%',\r\n\t\t\t\t\t\t\t\tbackgroundColor: isDragging ? '#ffaa00' : 'rgba(255, 170, 0, 0.8)',\r\n\t\t\t\t\t\t\t\tborder: '2px solid white',\r\n\t\t\t\t\t\t\t\tcursor: isDragging ? 'grabbing' : 'grab',\r\n\t\t\t\t\t\t\t\tpointerEvents: 'auto',\r\n\t\t\t\t\t\t\t\tboxShadow: '0 2px 4px rgba(0,0,0,0.3)',\r\n\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t{/* 라벨 */}\r\n\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\tstyle={{\r\n\t\t\t\t\t\t\t\tposition: 'absolute',\r\n\t\t\t\t\t\t\t\ttop: -24,\r\n\t\t\t\t\t\t\t\tleft: '50%',\r\n\t\t\t\t\t\t\t\ttransform: 'translateX(-50%)',\r\n\t\t\t\t\t\t\t\tfontSize: 10,\r\n\t\t\t\t\t\t\t\tcolor: '#ffaa00',\r\n\t\t\t\t\t\t\t\tfontWeight: 'bold',\r\n\t\t\t\t\t\t\t\ttextShadow: '1px 1px 2px rgba(0,0,0,0.8)',\r\n\t\t\t\t\t\t\t\twhiteSpace: 'nowrap',\r\n\t\t\t\t\t\t\t\tpointerEvents: 'none',\r\n\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t✨\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t);\r\n\t\t\t})}\r\n\t\t</div>\r\n\t);\r\n};\r\n","import React from 'react';\nimport { DistortionArea } from '../../types/area';\n\nexport interface AreaListProps {\n\tareas: DistortionArea[];\n\tselectedAreaId: string | null;\n\tonSelectArea: (areaId: string) => void;\n\tonRemoveArea: (areaId: string) => void;\n\tonAddArea: () => void;\n}\n\nexport const AreaList: React.FC<AreaListProps> = ({\n\tareas,\n\tselectedAreaId,\n\tonSelectArea,\n\tonRemoveArea,\n\tonAddArea,\n}) => {\n\treturn (\n\t\t<div className=\"area-list\">\n\t\t\t<div className=\"area-list-header\">\n\t\t\t\t<h3>왜곡 영역</h3>\n\t\t\t\t<button\n\t\t\t\t\tonClick={onAddArea}\n\t\t\t\t\tdisabled={areas.length >= 8}\n\t\t\t\t\tclassName=\"btn-add\"\n\t\t\t\t\ttitle={areas.length >= 8 ? '최대 8개 영역까지 지원' : '새 영역 추가'}\n\t\t\t\t>\n\t\t\t\t\t+ 추가\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<div className=\"area-list-items\">\n\t\t\t\t{areas.length === 0 ? (\n\t\t\t\t\t<div className=\"area-list-empty\">영역이 없습니다. + 추가 버튼을 눌러주세요.</div>\n\t\t\t\t) : (\n\t\t\t\t\tareas.map((area, index) => (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tkey={area.id}\n\t\t\t\t\t\t\tclassName={`area-item ${selectedAreaId === area.id ? 'selected' : ''}`}\n\t\t\t\t\t\t\tonClick={() => onSelectArea(area.id)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div className=\"area-item-info\">\n\t\t\t\t\t\t\t\t<span className=\"area-item-name\">영역 {index + 1}</span>\n\t\t\t\t\t\t\t\t<span className=\"area-item-strength\">강도: {(area.distortionStrength * 100).toFixed(0)}%</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\t\t\tonRemoveArea(area.id);\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tclassName=\"btn-remove\"\n\t\t\t\t\t\t\t\ttitle=\"영역 삭제\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t×\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n","import React from 'react';\nimport { DistortionArea, EasingFunction } from '../../types/area';\n\nexport interface ParameterPanelProps {\n\tarea: DistortionArea | null;\n\tonUpdateArea: (updates: Partial<DistortionArea>) => void;\n}\n\nconst EASING_OPTIONS: { value: EasingFunction; label: string }[] = [\n\t{ value: 'linear', label: '선형 (Linear)' },\n\t{ value: 'easeIn', label: '가속 (Ease In)' },\n\t{ value: 'easeOut', label: '감속 (Ease Out)' },\n\t{ value: 'easeInOut', label: '가감속 (Ease In Out)' },\n\t{ value: 'easeInQuad', label: '가속² (Ease In Quad)' },\n\t{ value: 'easeOutQuad', label: '감속² (Ease Out Quad)' },\n];\n\nexport const ParameterPanel: React.FC<ParameterPanelProps> = ({ area, onUpdateArea }) => {\n\tif (!area) {\n\t\treturn (\n\t\t\t<div className=\"parameter-panel\">\n\t\t\t\t<div className=\"parameter-panel-empty\">영역을 선택해주세요</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"parameter-panel\">\n\t\t\t<h3>파라미터 편집</h3>\n\n\t\t\t{/* 왜곡 강도 */}\n\t\t\t<div className=\"parameter-group\">\n\t\t\t\t<label>\n\t\t\t\t\t왜곡 강도: {(area.distortionStrength * 100).toFixed(0)}%\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\ttype=\"range\"\n\t\t\t\t\tmin=\"0\"\n\t\t\t\t\tmax=\"1\"\n\t\t\t\t\tstep=\"0.01\"\n\t\t\t\t\tvalue={area.distortionStrength}\n\t\t\t\t\tonChange={(e) => onUpdateArea({ distortionStrength: parseFloat(e.target.value) })}\n\t\t\t\t\tclassName=\"slider\"\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t{/* 애니메이션 지속 시간 */}\n\t\t\t<div className=\"parameter-group\">\n\t\t\t\t<label>\n\t\t\t\t\t지속 시간: {area.movement.duration.toFixed(1)}초\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tmin=\"0.1\"\n\t\t\t\t\tmax=\"10\"\n\t\t\t\t\tstep=\"0.1\"\n\t\t\t\t\tvalue={area.movement.duration}\n\t\t\t\t\tonChange={(e) =>\n\t\t\t\t\t\tonUpdateArea({\n\t\t\t\t\t\t\tmovement: { ...area.movement, duration: parseFloat(e.target.value) },\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tclassName=\"input-number\"\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t{/* 이징 함수 */}\n\t\t\t<div className=\"parameter-group\">\n\t\t\t\t<label>이징 함수</label>\n\t\t\t\t<select\n\t\t\t\t\tvalue={area.movement.easing}\n\t\t\t\t\tonChange={(e) =>\n\t\t\t\t\t\tonUpdateArea({\n\t\t\t\t\t\t\tmovement: { ...area.movement, easing: e.target.value as EasingFunction },\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tclassName=\"select\"\n\t\t\t\t>\n\t\t\t\t\t{EASING_OPTIONS.map((option) => (\n\t\t\t\t\t\t<option key={option.value} value={option.value}>\n\t\t\t\t\t\t\t{option.label}\n\t\t\t\t\t\t</option>\n\t\t\t\t\t))}\n\t\t\t\t</select>\n\t\t\t</div>\n\n\t\t\t{/* 렌즈 효과 */}\n\t\t\t<div className=\"parameter-group\">\n\t\t\t\t<label>\n\t\t\t\t\t렌즈 효과: {((area.lensEffect?.strength ?? 0) > 0 ? '볼록 ' : (area.lensEffect?.strength ?? 0) < 0 ? '오목 ' : '')}{((area.lensEffect?.strength ?? 0) * 100).toFixed(0)}%\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\ttype=\"range\"\n\t\t\t\t\tmin=\"-1\"\n\t\t\t\t\tmax=\"1\"\n\t\t\t\t\tstep=\"0.01\"\n\t\t\t\t\tvalue={area.lensEffect?.strength ?? 0}\n\t\t\t\t\tonChange={(e) => onUpdateArea({ lensEffect: { strength: parseFloat(e.target.value) } })}\n\t\t\t\t\tclassName=\"slider\"\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t{/* 스텝 양자화 */}\n\t\t\t<div className=\"parameter-group\">\n\t\t\t\t<label>\n\t\t\t\t\t움직임 단계: {(area.snapSteps ?? 0) === 0 ? '없음' : `${area.snapSteps}단계`}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\ttype=\"range\"\n\t\t\t\t\tmin=\"0\"\n\t\t\t\t\tmax=\"5\"\n\t\t\t\t\tstep=\"1\"\n\t\t\t\t\tvalue={area.snapSteps ?? 0}\n\t\t\t\t\tonChange={(e) => onUpdateArea({ snapSteps: parseInt(e.target.value) })}\n\t\t\t\t\tclassName=\"slider\"\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t{/* 포인트 좌표 (읽기 전용 표시) */}\n\t\t\t<div className=\"parameter-group\">\n\t\t\t\t<label>포인트 좌표 (캔버스에서 드래그)</label>\n\t\t\t\t<div className=\"points-display\">\n\t\t\t\t\t{area.basePoints.map((point, idx) => (\n\t\t\t\t\t\t<div key={idx} className=\"point-coord\">\n\t\t\t\t\t\t\tP{idx + 1}: ({point.x.toFixed(3)}, {point.y.toFixed(3)})\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n","import { useState, useCallback } from 'react';\r\nimport { DistortionArea, Point } from '../../types/area';\r\nimport { EditorState } from '../types';\r\n\r\nexport const useDistortionEditor = (initialAreas: DistortionArea[] = []) => {\r\n\tconst [state, setState] = useState<EditorState>({\r\n\t\tselectedAreaId: initialAreas[0]?.id || null,\r\n\t\tareas: initialAreas,\r\n\t\teditMode: 'normal',\r\n\t\tdraggingPointIndex: null,\r\n\t});\r\n\r\n\t/** 영역 선택 */\r\n\tconst selectArea = useCallback((areaId: string | null) => {\r\n\t\tsetState((prev) => ({ ...prev, selectedAreaId: areaId }));\r\n\t}, []);\r\n\r\n\t/** 영역 추가 */\r\n\tconst addArea = useCallback((area: DistortionArea) => {\r\n\t\tsetState((prev) => ({\r\n\t\t\t...prev,\r\n\t\t\tareas: [...prev.areas, area],\r\n\t\t\tselectedAreaId: area.id,\r\n\t\t}));\r\n\t}, []);\r\n\r\n\t/** 영역 삭제 */\r\n\tconst removeArea = useCallback((areaId: string) => {\r\n\t\tsetState((prev) => {\r\n\t\t\tconst newAreas = prev.areas.filter((a) => a.id !== areaId);\r\n\t\t\treturn {\r\n\t\t\t\t...prev,\r\n\t\t\t\tareas: newAreas,\r\n\t\t\t\tselectedAreaId:\r\n\t\t\t\t\tprev.selectedAreaId === areaId ? newAreas[0]?.id || null : prev.selectedAreaId,\r\n\t\t\t};\r\n\t\t});\r\n\t}, []);\r\n\r\n\t/** 영역 업데이트 */\r\n\tconst updateArea = useCallback((areaId: string, updates: Partial<DistortionArea>) => {\r\n\t\tsetState((prev) => ({\r\n\t\t\t...prev,\r\n\t\t\tareas: prev.areas.map((area) => (area.id === areaId ? { ...area, ...updates } : area)),\r\n\t\t}));\r\n\t}, []);\r\n\r\n\t/** 포인트 업데이트 */\r\n\tconst updatePoint = useCallback((areaId: string, pointIndex: number, point: Point) => {\r\n\t\tsetState((prev) => ({\r\n\t\t\t...prev,\r\n\t\t\tareas: prev.areas.map((area) => {\r\n\t\t\t\tif (area.id === areaId) {\r\n\t\t\t\t\tconst newPoints = [...area.basePoints] as [Point, Point, Point, Point];\r\n\t\t\t\t\tnewPoints[pointIndex] = point;\r\n\t\t\t\t\treturn { ...area, basePoints: newPoints };\r\n\t\t\t\t}\r\n\t\t\t\treturn area;\r\n\t\t\t}),\r\n\t\t}));\r\n\t}, []);\r\n\r\n\t/** 드래그 시작 */\r\n\tconst startDragging = useCallback((pointIndex: number) => {\r\n\t\tsetState((prev) => ({ ...prev, draggingPointIndex: pointIndex }));\r\n\t}, []);\r\n\r\n\t/** 드래그 종료 */\r\n\tconst stopDragging = useCallback(() => {\r\n\t\tsetState((prev) => ({ ...prev, draggingPointIndex: null }));\r\n\t}, []);\r\n\r\n\t/** 편집 모드 변경 */\r\n\tconst setEditMode = useCallback((mode: EditorState['editMode']) => {\r\n\t\tsetState((prev) => ({ ...prev, editMode: mode }));\r\n\t}, []);\r\n\r\n\t/** 선택된 영역 가져오기 */\r\n\tconst getSelectedArea = useCallback(() => {\r\n\t\treturn state.areas.find((a) => a.id === state.selectedAreaId) || null;\r\n\t}, [state.areas, state.selectedAreaId]);\r\n\r\n\treturn {\r\n\t\tstate,\r\n\t\tselectArea,\r\n\t\taddArea,\r\n\t\tremoveArea,\r\n\t\tupdateArea,\r\n\t\tupdatePoint,\r\n\t\tstartDragging,\r\n\t\tstopDragging,\r\n\t\tsetEditMode,\r\n\t\tgetSelectedArea,\r\n\t};\r\n};\r\n","import { EditorCanvasStyle } from './types';\n\n/**\n * 기본 에디터 캔버스 스타일\n */\nexport const DEFAULT_EDITOR_CANVAS_STYLE: EditorCanvasStyle = {\n\t// 3단계 원 스타일 (외부 -> 내부)\n\tcircleLevels: [\n\t\t{\n\t\t\tradius: 0.5,\n\t\t\topacity: 0.3,\n\t\t\tlineWidth: 2,\n\t\t\tcolor: 'rgba(255, 200, 0, 1)',\n\t\t\tdashPattern: [8, 4],\n\t\t},\n\t\t{\n\t\t\tradius: 0.33,\n\t\t\topacity: 0.6,\n\t\t\tlineWidth: 2.5,\n\t\t\tcolor: 'rgba(255, 200, 0, 1)',\n\t\t\tdashPattern: [8, 4],\n\t\t},\n\t\t{\n\t\t\tradius: 0.167,\n\t\t\topacity: 0.9,\n\t\t\tlineWidth: 3,\n\t\t\tcolor: 'rgba(255, 200, 0, 1)',\n\t\t\tdashPattern: [8, 4],\n\t\t},\n\t],\n\t// 원 내부 채우기\n\tcircleFillColor: 'rgba(255, 200, 0, 0.08)',\n\t// 중심점\n\tcenterPoint: {\n\t\tradius: 5,\n\t\tfillColor: 'rgba(255, 200, 0, 1)',\n\t\tstrokeColor: 'rgba(255, 255, 255, 0.8)',\n\t\tstrokeWidth: 2,\n\t},\n\t// 포인트 핸들\n\tpointHandle: {\n\t\tsize: 16,\n\t\tfillColor: '#00aaff',\n\t\tstrokeColor: 'white',\n\t\tstrokeWidth: 2,\n\t\tlabelColor: '#00aaff',\n\t\tlabelFontSize: 11,\n\t},\n\t// 영역 외곽선\n\tareaOutline: {\n\t\tselectedColor: '#00aaff',\n\t\tunselectedColor: '#888',\n\t\tselectedWidth: 2,\n\t\tunselectedWidth: 1,\n\t\tunselectedDashPattern: [5, 5],\n\t\tselectedFillColor: 'rgba(0, 170, 255, 0.08)', // 선택된 영역 배경 (연한 파란색)\n\t\tunselectedFillColor: 'rgba(136, 136, 136, 0.03)', // 선택 안된 영역 배경 (연한 회색)\n\t},\n};\n"],"mappings":"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","names":["useEffect","useRef","useState","useCallback","THREE","THREE","THREE","useRef","useCallback","useRef","useEffect","useRef","useCallback","useRef","useState","useEffect","useCallback","useRef","useEffect","useState","useCallback","jsx","jsx","jsxs","useState","useCallback","jsx","jsxs","useRef","useState","useEffect","isPointInPolygon","useCallback"]} |