BaekRyang e08f34caab feat: Add per-area physics configuration
- 영역별 물리 설정 `physics` 옵션을 추가했습니다.
- `useMouseInteraction` 훅에서 영역별 물리 설정을 적용하도록 수정했습니다.
- `ImageDistortion` 컴포넌트에서 `selectedAreaId` prop을 제거하고, 마우스 인터랙션 시 `interactingAreaIndices`를 활용하도록 변경했습니다.
- 셰이더에서 텍스처 좌표가 범위를 벗어날 때 부드럽게 페이드 아웃되도록 수정했습니다.
2025-11-24 14:23:06 +09:00

573 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React$1 from 'react';
import * as THREE from 'three';
/**
* 정규화된 좌표계의 2D 포인트 (0.0 - 1.0)
*/
interface Point {
x: number;
y: number;
}
/**
* 애니메이션 이징 함수 타입
*/
type EasingFunction = 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'easeInQuad' | 'easeOutQuad';
/**
* 모션 프리셋 타입
*/
type MotionPreset = 'none' | 'horizontal' | 'vertical' | 'rotate-cw' | 'rotate-ccw' | 'pulse' | 'diagonal-1' | 'diagonal-2';
/**
* 왜곡 애니메이션 움직임 설정
*/
interface DistortionMovement {
/** 모션 프리셋 (vectorA, vectorB 대신 사용) */
preset?: MotionPreset;
/** 왜곡 시작 벡터 (preset 없을 때 사용) */
vectorA: Point;
/** 왜곡 종료 벡터 (preset 없을 때 사용, 현재는 미사용) */
vectorB: Point;
/** 애니메이션 지속 시간 (초) */
duration: number;
/** 적용할 이징 함수 */
easing: EasingFunction;
/** 모션 강도 (프리셋 적용 시 벡터 크기 조절용, 기본값: 0.1) */
strength?: number;
}
/**
* 사각형 포인트와 애니메이션 설정을 포함하는 왜곡 영역
*/
interface DistortionArea {
/** 고유 식별자 */
id: string;
/** 사각형의 네 모서리 포인트 [topLeft, topRight, bottomRight, bottomLeft] */
basePoints: [Point, Point, Point, Point];
/** 움직임 애니메이션 설정 */
movement: DistortionMovement;
/** 왜곡 강도 (0.0 - 1.0) */
distortionStrength: number;
/** 현재 애니메이션 진행도 (0.0 - 1.0) */
progress: number;
/** 현재 드래그 벡터 (progress로부터 계산됨) */
dragVector: Point;
/** 영역별 물리 설정 (선택사항, 마우스 인터랙션 시 사용) */
physics?: {
stiffness: number;
damping: number;
mass: number;
influenceRadius: number;
maxStrength: number;
};
}
/**
* 영역 충돌 감지를 위한 경계 상자
*/
interface AreaBounds {
minX: number;
minY: number;
maxX: number;
maxY: number;
}
/**
* 셰이더 유니폼 변수 타입
*/
interface ShaderUniforms {
[uniform: string]: THREE.IUniform;
/** 화면 해상도 */
u_resolution: THREE.IUniform<THREE.Vector2>;
/** 이미지 텍스처 */
u_texture: THREE.IUniform<THREE.Texture | null>;
/** 모든 영역의 포인트 배열 (최대 32개 포인트 = 8영역 × 4포인트) */
u_points: THREE.IUniform<Float32Array>;
/** 활성 영역 개수 */
u_numAreas: THREE.IUniform<number>;
/** 각 영역의 드래그 벡터 배열 */
u_dragVectors: THREE.IUniform<Float32Array>;
/** 각 영역의 왜곡 강도 배열 */
u_distortionStrengths: THREE.IUniform<Float32Array>;
}
/**
* 셰이더 설정
*/
interface ShaderConfig {
/** 최대 영역 개수 */
maxAreas: number;
/** 최대 포인트 개수 (maxAreas × 4) */
maxPoints: number;
}
/**
* 애니메이션 상태
*/
interface AnimationState {
/** 재생 중 여부 */
isPlaying: boolean;
/** 현재 시간 (초) */
currentTime: number;
/** 델타 타임 (프레임 간 시간 차이, 초) */
deltaTime: number;
/** 현재 FPS */
fps: number;
}
/**
* 애니메이션 틱 컨트롤러
*/
interface AnimationTicker {
/** 애니메이션 시작 */
start: () => void;
/** 애니메이션 정지 */
stop: () => void;
/** 애니메이션 일시정지 */
pause: () => void;
/** 애니메이션 재개 */
resume: () => void;
}
/**
* 스프링 물리 파라미터
*/
interface SpringPhysicsConfig {
/** 스프링 탄성 계수 (높을수록 빠르게 복원) */
stiffness: number;
/** 감쇠 계수 (높을수록 빨리 멈춤) */
damping: number;
/** 질량 (높을수록 느리게 움직임) */
mass: number;
/** 영향 반경 (정규화 좌표, 기본값 0.2) */
influenceRadius: number;
/** 최대 왜곡 강도 */
maxStrength: number;
}
/**
* 마우스 인터랙션 설정
*/
interface MouseInteractionConfig {
/** 마우스 인터랙션 활성화 여부 */
enabled: boolean;
/** 스프링 물리 파라미터 */
physics: SpringPhysicsConfig;
/** 최소 속도 임계값 (이보다 느리면 효과 없음) */
minVelocity?: number;
/** 최대 속도 제한 (이보다 빠르면 클램핑) */
maxVelocity?: number;
/** 속도 승수 (마우스 속도에 곱해지는 값) */
velocityMultiplier?: number;
}
/**
* 마우스 상태
*/
interface MouseState {
/** 현재 마우스 위치 (정규화 좌표) */
position: Point | null;
/** 이전 마우스 위치 */
prevPosition: Point | null;
/** 속도 벡터 */
velocity: Point;
/** 가속도 벡터 */
acceleration: Point;
/** 마우스가 컨테이너 위에 있는지 */
isHovering: boolean;
/** 드래그 중인지 */
isDragging: boolean;
}
/**
* 스프링 물리 상태
*/
interface SpringState {
/** 현재 변위 (displacement) */
displacement: Point;
/** 현재 속도 */
velocity: Point;
/** 목표 위치 (평형 상태는 {x:0, y:0}) */
target: Point;
}
/**
* ImageDistortion 컴포넌트 Props
*/
interface ImageDistortionProps {
/** 이미지 소스 URL */
imageSrc: string;
/** 왜곡 영역 배열 */
areas: DistortionArea[];
/** 버텍스 셰이더 경로 (선택사항) */
vertexShaderPath?: string;
/** 프래그먼트 셰이더 경로 (선택사항) */
fragmentShaderPath?: string;
/** 애니메이션 재생 여부 */
isPlaying?: boolean;
/** 컨테이너 스타일 */
style?: React$1.CSSProperties;
/** 컨테이너 클래스명 */
className?: string;
/** 마우스 인터랙션 설정 */
mouseInteraction?: MouseInteractionConfig;
}
/**
* GPU 가속 이미지 왜곡 컴포넌트
* Three.js와 GLSL 셰이더를 사용하여 실시간 이미지 왜곡 효과를 제공합니다.
*/
declare const ImageDistortion: React$1.FC<ImageDistortionProps>;
/**
* 에디터 편집 모드
*/
type EditMode = 'normal' | 'point-edit' | 'parameter-edit';
/**
* 에디터 상태
*/
interface EditorState {
/** 현재 선택된 영역 ID */
selectedAreaId: string | null;
/** 모든 왜곡 영역 */
areas: DistortionArea[];
/** 현재 편집 모드 */
editMode: EditMode;
/** 드래그 중인 포인트 인덱스 (0-3) */
draggingPointIndex: number | null;
}
/**
* 왜곡 영역 원 레벨 스타일
*/
interface CircleLevelStyle {
/** 반지름 (0.0 - 1.0, UV 좌표) */
radius: number;
/** 투명도 (0.0 - 1.0) */
opacity: number;
/** 선 두께 (픽셀) */
lineWidth: number;
/** 선 색상 (CSS color) */
color?: string;
/** 대시 패턴 [dash, gap] */
dashPattern?: [number, number];
}
/**
* 중심점 스타일
*/
interface CenterPointStyle {
/** 반지름 (픽셀) */
radius?: number;
/** 채우기 색상 */
fillColor?: string;
/** 테두리 색상 */
strokeColor?: string;
/** 테두리 두께 */
strokeWidth?: number;
}
/**
* 포인트 핸들 스타일
*/
interface PointHandleStyle {
/** 핸들 크기 (픽셀) */
size?: number;
/** 채우기 색상 */
fillColor?: string;
/** 테두리 색상 */
strokeColor?: string;
/** 테두리 두께 */
strokeWidth?: number;
/** 레이블 색상 */
labelColor?: string;
/** 레이블 폰트 크기 */
labelFontSize?: number;
}
/**
* 영역 외곽선 스타일
*/
interface AreaOutlineStyle {
/** 선택된 영역 색상 */
selectedColor?: string;
/** 선택되지 않은 영역 색상 */
unselectedColor?: string;
/** 선택된 영역 선 두께 */
selectedWidth?: number;
/** 선택되지 않은 영역 선 두께 */
unselectedWidth?: number;
/** 선택되지 않은 영역 대시 패턴 */
unselectedDashPattern?: [number, number];
/** 선택된 영역 배경 채우기 색상 */
selectedFillColor?: string;
/** 선택되지 않은 영역 배경 채우기 색상 */
unselectedFillColor?: string;
}
/**
* 에디터 캔버스 스타일
*/
interface EditorCanvasStyle {
/** 왜곡 영역 원 레벨 스타일 배열 (외부 -> 내부 순) */
circleLevels?: CircleLevelStyle[];
/** 왜곡 영역 내부 채우기 색상 */
circleFillColor?: string;
/** 중심점 스타일 */
centerPoint?: CenterPointStyle;
/** 포인트 핸들 스타일 */
pointHandle?: PointHandleStyle;
/** 영역 외곽선 스타일 */
areaOutline?: AreaOutlineStyle;
}
/**
* 에디터 Props
*/
interface DistortionEditorProps {
/** 초기 영역 배열 */
initialAreas?: DistortionArea[];
/** 이미지 소스 */
imageSrc: string;
/** 영역 변경 콜백 */
onAreasChange?: (areas: DistortionArea[]) => void;
/** 선택된 영역 변경 콜백 */
onSelectedAreaChange?: (areaId: string | null) => void;
/** 캔버스 너비 */
width?: number;
/** 캔버스 높이 */
height?: number;
/** 에디터 캔버스 스타일 커스터마이징 */
canvasStyle?: EditorCanvasStyle;
}
declare const DistortionEditor: React$1.FC<DistortionEditorProps>;
declare const useDistortionEditor: (initialAreas?: DistortionArea[]) => {
state: EditorState;
selectArea: (areaId: string | null) => void;
addArea: (area: DistortionArea) => void;
removeArea: (areaId: string) => void;
updateArea: (areaId: string, updates: Partial<DistortionArea>) => void;
updatePoint: (areaId: string, pointIndex: number, point: Point) => void;
startDragging: (pointIndex: number) => void;
stopDragging: () => void;
setEditMode: (mode: EditorState["editMode"]) => void;
getSelectedArea: () => DistortionArea | null;
};
/**
* 진행도에 이징 함수를 적용
* @param progress 진행도 (0.0 - 1.0)
* @param easingType 적용할 이징 함수 타입
* @returns 이징이 적용된 진행도 (0.0 - 1.0)
*/
declare const applyEasing: (progress: number, easingType: EasingFunction) => number;
/**
* 셰이더 관련 설정
*/
declare const SHADER_CONFIG: {
/** 최대 영역 개수 */
readonly MAX_AREAS: 8;
/** 최대 포인트 개수 (8영역 × 4포인트) */
readonly MAX_POINTS: 32;
/** 최대 드래그 벡터 개수 */
readonly MAX_DRAG_VECTORS: 8;
/** 최대 강도 배열 크기 */
readonly MAX_STRENGTHS: 8;
};
/**
* 애니메이션 관련 설정
*/
declare const ANIMATION_CONFIG: {
/** 목표 FPS */
readonly TARGET_FPS: 60;
/** 델타 타임 (약 16.67ms) */
readonly DELTA_TIME: number;
};
/**
* 기본 영역 설정값
*/
declare const DEFAULT_AREA: {
/** 기본 왜곡 강도 */
readonly DISTORTION_STRENGTH: 0.5;
/** 기본 애니메이션 지속 시간 (초) */
readonly DURATION: 2;
/** 기본 이징 함수 */
readonly EASING: "easeInOut";
/** 기본 벡터 A */
readonly VECTOR_A: {
readonly x: 0.1;
readonly y: 0.1;
};
/** 기본 벡터 B */
readonly VECTOR_B: {
readonly x: -0.1;
readonly y: -0.1;
};
};
/**
* 모션 프리셋을 벡터로 변환
* @param preset 모션 프리셋
* @param strength 모션 강도 (기본값: 0.1)
* @returns 계산된 벡터 (vectorA)
*/
declare function presetToVector(preset: MotionPreset, strength?: number): Point;
/**
* 프리셋이 회전 타입인지 확인
*/
declare function isRotationPreset(preset?: MotionPreset): boolean;
/**
* Three.js 씬 관리 클래스
*/
declare class ThreeScene {
private container;
private scene;
private camera;
private renderer;
private mesh;
private uniforms;
constructor(container: HTMLElement);
/**
* 윈도우 리사이즈 핸들러
*/
private handleResize;
/**
* 셰이더 머티리얼 설정
* @param vertexShader 버텍스 셰이더 소스
* @param fragmentShader 프래그먼트 셰이더 소스
*/
setShaderMaterial(vertexShader: string, fragmentShader: string): void;
/**
* 유니폼 값 업데이트
* @param updates 업데이트할 유니폼 값들
*/
updateUniforms(updates: Partial<ShaderUniforms>): void;
/**
* 씬 렌더링
*/
render(): void;
/**
* 현재 해상도 가져오기
*/
getResolution(): {
x: number;
y: number;
};
/**
* 리소스 정리
*/
dispose(): void;
}
/**
* 셰이더 파일 로딩 및 관리 클래스
*/
declare class ShaderManager {
private vertexShaderSource;
private fragmentShaderSource;
/**
* 셰이더 파일들을 비동기로 로드
* @param vertexPath 버텍스 셰이더 파일 경로
* @param fragmentPath 프래그먼트 셰이더 파일 경로
* @returns 로드된 셰이더 소스 코드
*/
loadShaders(vertexPath: string, fragmentPath: string): Promise<{
vertex: string;
fragment: string;
}>;
/**
* 버텍스 셰이더 소스 코드 반환
*/
getVertexShader(): string;
/**
* 프래그먼트 셰이더 소스 코드 반환
*/
getFragmentShader(): string;
}
/**
* 애니메이션 루프 관리 클래스
*/
declare class AnimationLoop {
/**
* 영역들의 드래그 벡터를 현재 진행도에 따라 업데이트
* @param areas 왜곡 영역 배열
* @returns 업데이트된 영역 배열
*/
static updateAreaDragVectors(areas: DistortionArea[]): DistortionArea[];
/**
* 모든 영역의 진행도를 델타 타임만큼 업데이트
* @param areas 왜곡 영역 배열
* @param deltaTime 델타 타임 (초)
* @returns 업데이트된 영역 배열
*/
static updateProgress(areas: DistortionArea[], deltaTime: number): DistortionArea[];
}
/**
* 스프링 기반 물리 시뮬레이션 엔진
* Hooke's Law와 감쇠를 적용한 스프링-댐퍼 시스템
*/
declare class SpringPhysics {
private config;
private state;
constructor(config: SpringPhysicsConfig);
/**
* 물리 파라미터 업데이트
*/
setConfig(config: Partial<SpringPhysicsConfig>): void;
/**
* 목표 위치 설정 (마우스 속도 기반)
*/
setTarget(velocity: Point, velocityMultiplier?: number): void;
/**
* 초기 속도 설정 (드래그 방향과 속도를 즉시 반영)
* 드래그 방향으로 즉시 튕기는 효과
*/
setInitialVelocity(velocity: Point, multiplier?: number): void;
/**
* 스프링 물리 업데이트 (Hooke's Law + Damping)
* F = -k * x - c * v
* a = F / m
* v += a * dt
* x += v * dt
*/
update(deltaTime: number): Point;
/**
* 즉시 충격 적용 (마우스 가속도 기반)
*/
applyImpulse(acceleration: Point, multiplier?: number): void;
/**
* 현재 변위 가져오기
*/
getDisplacement(): Point;
/**
* 현재 속도 가져오기
*/
getVelocity(): Point;
/**
* 상태 리셋
*/
reset(): void;
/**
* 마우스가 멈췄을 때 목표를 0으로 설정 (평형 상태로 복귀)
*/
returnToEquilibrium(): void;
}
/**
* requestAnimationFrame을 사용한 애니메이션 루프 훅
* @param callback 매 프레임마다 호출될 콜백 (deltaTime을 인자로 받음)
* @param isPlaying 애니메이션 재생 여부
*/
declare const useAnimationFrame: (callback: (deltaTime: number) => void, isPlaying?: boolean) => void;
/**
* 마우스 위치, 속도, 가속도를 추적하는 훅
*/
declare const useMouseVelocity: (containerRef: React.RefObject<HTMLElement | null>) => {
getState: () => MouseState;
};
/**
* 마우스 인터랙션 기반 기존 영역 제어 훅
* 마우스가 지나가는 모든 영역에 효과 적용
*/
declare const useMouseInteraction: (containerRef: React.RefObject<HTMLElement | null>, config: MouseInteractionConfig) => {
updateInteraction: (areas: DistortionArea[], deltaTime: number) => DistortionArea[];
updateConfig: (newConfig: Partial<MouseInteractionConfig>) => void;
reset: () => void;
isDragging: () => boolean;
getInteractingAreaIndices: () => Set<number>;
};
export { ANIMATION_CONFIG, AnimationLoop, type AnimationState, type AnimationTicker, type AreaBounds, DEFAULT_AREA, type DistortionArea, DistortionEditor, type DistortionEditorProps, type DistortionMovement, type EasingFunction, type EditMode, type EditorState, ImageDistortion, type ImageDistortionProps, type MotionPreset, type MouseInteractionConfig, type MouseState, type Point, SHADER_CONFIG, type ShaderConfig, ShaderManager, type ShaderUniforms, SpringPhysics, type SpringPhysicsConfig, type SpringState, ThreeScene, applyEasing, isRotationPreset, presetToVector, useAnimationFrame, useDistortionEditor, useMouseInteraction, useMouseVelocity };