bowling-analysis-system
Version:
A comprehensive system for analyzing bowling techniques using video processing and metrics calculation
152 lines (124 loc) • 4.42 kB
JavaScript
/**
* @module metrics/calculations/angles/SpineAngles
* @description Functions for calculating spine and trunk angles
*/
const { calculateAngleBetweenVectors, getSafeLandmark, createVector } = require('../MetricsUtilities');
/**
* Calculate spine twist angle
* @param {Array} landmarks - Array of pose landmarks
* @returns {number} Spine twist angle in degrees
*/
function calculateSpineTwist(landmarks) {
const leftShoulder = getSafeLandmark(landmarks, 11);
const rightShoulder = getSafeLandmark(landmarks, 12);
const leftHip = getSafeLandmark(landmarks, 23);
const rightHip = getSafeLandmark(landmarks, 24);
if (!leftShoulder || !rightShoulder || !leftHip || !rightHip) return null;
const shoulderLine = createVector(leftShoulder, rightShoulder);
const hipLine = createVector(leftHip, rightHip);
// Project both lines onto horizontal plane
const shoulderHorizontal = {
x: shoulderLine.x,
y: 0,
z: shoulderLine.z
};
const hipHorizontal = {
x: hipLine.x,
y: 0,
z: hipLine.z
};
return calculateAngleBetweenVectors(shoulderHorizontal, hipHorizontal);
}
/**
* Calculate trunk flexion angle
* @param {Array} landmarks - Array of pose landmarks
* @returns {number} Trunk flexion angle in degrees
*/
function calculateTrunkFlexion(landmarks) {
const neck = getSafeLandmark(landmarks, 0);
const midHip = getCenterPoint(
getSafeLandmark(landmarks, 23),
getSafeLandmark(landmarks, 24)
);
if (!neck || !midHip) return null;
const trunk = createVector(midHip, neck);
const vertical = { x: 0, y: 1, z: 0 };
return calculateAngleBetweenVectors(trunk, vertical);
}
/**
* Calculate trunk lean angle
* @param {Array} landmarks - Array of pose landmarks
* @returns {number} Trunk lean angle in degrees
*/
function calculateTrunkLean(landmarks) {
const leftShoulder = getSafeLandmark(landmarks, 11);
const rightShoulder = getSafeLandmark(landmarks, 12);
const leftHip = getSafeLandmark(landmarks, 23);
const rightHip = getSafeLandmark(landmarks, 24);
if (!leftShoulder || !rightShoulder || !leftHip || !rightHip) return null;
// Calculate midpoints
const midShoulder = getCenterPoint(leftShoulder, rightShoulder);
const midHip = getCenterPoint(leftHip, rightHip);
if (!midShoulder || !midHip) return null;
const trunk = createVector(midHip, midShoulder);
const vertical = { x: 0, y: 1, z: 0 };
return calculateAngleBetweenVectors(trunk, vertical);
}
/**
* Calculate spine angle
* @param {Array} landmarks - Array of pose landmarks
* @returns {number} Spine angle in degrees
*/
function calculateSpineAngle(landmarks) {
const neck = getSafeLandmark(landmarks, 0);
const midShoulder = getCenterPoint(
getSafeLandmark(landmarks, 11),
getSafeLandmark(landmarks, 12)
);
const midHip = getCenterPoint(
getSafeLandmark(landmarks, 23),
getSafeLandmark(landmarks, 24)
);
if (!neck || !midShoulder || !midHip) return null;
const upperSpine = createVector(midShoulder, neck);
const lowerSpine = createVector(midHip, midShoulder);
return calculateAngleBetweenVectors(upperSpine, lowerSpine);
}
/**
* Calculate neck angle
* @param {Array} landmarks - Array of pose landmarks
* @returns {number} Neck angle in degrees
*/
function calculateNeckAngle(landmarks) {
const head = getSafeLandmark(landmarks, 0);
const neck = getSafeLandmark(landmarks, 1);
const midShoulder = getCenterPoint(
getSafeLandmark(landmarks, 11),
getSafeLandmark(landmarks, 12)
);
if (!head || !neck || !midShoulder) return null;
const neckLine = createVector(neck, head);
const upperSpine = createVector(midShoulder, neck);
return calculateAngleBetweenVectors(neckLine, upperSpine);
}
/**
* Get center point between two points
* @param {Object} point1 - First point
* @param {Object} point2 - Second point
* @returns {Object} Center point
*/
function getCenterPoint(point1, point2) {
if (!point1 || !point2) return null;
return {
x: (point1.x + point2.x) / 2,
y: (point1.y + point2.y) / 2,
z: (point1.z + point2.z) / 2
};
}
module.exports = {
calculateSpineTwist,
calculateTrunkFlexion,
calculateTrunkLean,
calculateSpineAngle,
calculateNeckAngle
};