Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | 4008x 4008x 4008x 4008x 4008x 4008x 4008x 4008x 4008x 4008x 4008x 4008x 2213x 4008x 4008x 2213x 4008x 2213x 4008x 4008x 4008x 4008x 2103x 4008x 4008x 4008x 4008x | /**
* Punch Phase Application Utilities
*
* Utilities for applying punch phase poses to keyframes with integrated
* anatomy awareness (hand poses and highlighting for strikes).
* 주먹 단계 적용 유틸리티 (해부학 통합)
*
* @module systems/animation/PunchPhaseApplicator
* @korean 주먹단계적용기
*/
import { BoneName } from "@/types/skeletal";
import type { HandHighlightMode, KeyframeConfig } from "./KeyframeConfig";
import { PUNCH_PHASES } from "./MartialArtsConstants";
/**
* Interface for punch phases with Korean martial arts biomechanics
* Includes opposite arm hikite (당기기) for power generation
*/
interface PunchPhase {
readonly shoulder: readonly [number, number, number];
readonly elbow: readonly [number, number, number];
readonly wrist?: readonly [number, number, number];
readonly spineY?: number;
readonly pelvisY?: number;
// Opposite arm for hikite (pulling hand) - 당기기
readonly oppositeShoulder?: readonly [number, number, number];
readonly oppositeElbow?: readonly [number, number, number];
readonly oppositeWrist?: readonly [number, number, number];
}
/** Phase name keys */
export type PunchPhaseName = keyof typeof PUNCH_PHASES;
/** Punch hand side */
export type PunchSide = "left" | "right";
/**
* Apply punch phase to a KeyframeConfig with Korean martial arts biomechanics
* and anatomy integration (hand poses, highlighting).
*
* Handles common punch phase bones: shoulder, elbow, wrist, spine, pelvis
* Now includes opposite arm hikite (당기기) and automatic hand pose/highlight
*
* @param kf - KeyframeConfig to apply phase to
* @param phase - Punch phase data from PUNCH_PHASES
* @param hand - Which hand is punching ("left" | "right")
* @param options - Configuration including anatomy options
*
* @example
* ```typescript
* // Apply extension with automatic fist pose and knuckle highlight
* applyPunchPhaseToConfig(kf, PUNCH_PHASES.EXTENSION, "right", {
* handPose: "fist",
* handHighlightMode: "knuckles",
* includeOppositeArm: true
* });
* ```
*
* @korean KeyframeConfig에주먹단계적용
*/
export function applyPunchPhaseToConfig(
kf: KeyframeConfig,
phase: PunchPhase,
hand: PunchSide = "right",
options: {
readonly includeWrist?: boolean;
readonly includeSpineMiddle?: boolean;
readonly includeOppositeArm?: boolean;
// Anatomy integration
readonly handPose?: string;
readonly handHighlightMode?: HandHighlightMode;
readonly oppositeHandPose?: string;
} = {}
): void {
const {
includeWrist = false,
includeSpineMiddle = false,
includeOppositeArm = true,
handPose,
handHighlightMode,
oppositeHandPose,
} = options;
// Select bones based on punching hand
const shoulderBone =
hand === "right" ? BoneName.SHOULDER_R : BoneName.SHOULDER_L;
const elbowBone = hand === "right" ? BoneName.ELBOW_R : BoneName.ELBOW_L;
const wristBone = hand === "right" ? BoneName.WRIST_R : BoneName.WRIST_L;
// Opposite hand bones for hikite (당기기)
const oppositeShoulderBone =
hand === "right" ? BoneName.SHOULDER_L : BoneName.SHOULDER_R;
const oppositeElbowBone =
hand === "right" ? BoneName.ELBOW_L : BoneName.ELBOW_R;
const oppositeWristBone =
hand === "right" ? BoneName.WRIST_L : BoneName.WRIST_R;
// Apply punching arm shoulder rotation
Eif (phase.shoulder) {
kf.rotate(
shoulderBone,
phase.shoulder[0],
phase.shoulder[1],
phase.shoulder[2]
);
}
// Apply punching arm elbow rotation
Eif (phase.elbow) {
kf.rotate(elbowBone, phase.elbow[0], phase.elbow[1], phase.elbow[2]);
}
// Optional punching arm wrist rotation (for fist rotation)
if (includeWrist && phase.wrist) {
kf.rotate(wristBone, phase.wrist[0], phase.wrist[1], phase.wrist[2]);
}
// Apply opposite arm hikite (당기기 - pulling hand for power generation)
Eif (includeOppositeArm) {
if (phase.oppositeShoulder) {
kf.rotate(
oppositeShoulderBone,
phase.oppositeShoulder[0],
phase.oppositeShoulder[1],
phase.oppositeShoulder[2]
);
}
if (phase.oppositeElbow) {
kf.rotate(
oppositeElbowBone,
phase.oppositeElbow[0],
phase.oppositeElbow[1],
phase.oppositeElbow[2]
);
}
Iif (includeWrist && phase.oppositeWrist) {
kf.rotate(
oppositeWristBone,
phase.oppositeWrist[0],
phase.oppositeWrist[1],
phase.oppositeWrist[2]
);
}
}
// Spine and pelvis Y-axis rotations for hip/shoulder engagement
Eif (phase.spineY !== undefined) {
kf.rotate(BoneName.SPINE_UPPER, 0, phase.spineY, 0);
if (includeSpineMiddle) {
kf.rotate(BoneName.SPINE_MIDDLE, 0, phase.spineY * 0.7, 0);
}
}
Eif (phase.pelvisY !== undefined) {
kf.rotate(BoneName.PELVIS, 0, phase.pelvisY, 0);
}
// ═══════════════════════════════════════════════════════════════════════════
// ANATOMY INTEGRATION (해부학 통합)
// ═══════════════════════════════════════════════════════════════════════════
// Set punching hand pose and highlight
Iif (handPose) {
if (hand === "right") {
kf.setRightHandPose(handPose, handHighlightMode);
} else {
kf.setLeftHandPose(handPose, handHighlightMode);
}
}
// Set opposite hand pose (guard position)
Iif (oppositeHandPose) {
if (hand === "right") {
kf.setLeftHandPose(oppositeHandPose);
} else {
kf.setRightHandPose(oppositeHandPose);
}
}
}
/**
* Get a punch phase by name
*
* @param phaseName - Name of the phase from PUNCH_PHASES
* @returns The punch phase data
*
* @korean 주먹단계가져오기
*/
export function getPunchPhase(phaseName: PunchPhaseName): PunchPhase {
return PUNCH_PHASES[phaseName];
}
|