Initialize repo

This commit is contained in:
Kazimierz Ciołek
2026-02-02 13:42:04 +01:00
commit 61e3482715
97 changed files with 156542 additions and 0 deletions

View File

@@ -0,0 +1,160 @@
import type { Point } from './usePostureAnalysis';
// Constants
export const THRESHOLDS = {
TILT_WARN: 1.5, // degrees
CVA_NORM: 50, // degrees
TRUNK_LEAN_WARN: 2.0 // degrees (estimate)
};
// --- Helper Math ---
const dist = (a: Point, b: Point) => Math.hypot(b.x - a.x, b.y - a.y);
// vector removed
const degrees = (radians: number) => radians * (180 / Math.PI);
// Angle of vector relative to horizontal (0 degrees = Right, 90 = Down in image coords usually, -90 Up)
// We want standard math: 0=Right, 90=Up?
// Image coords: Y grows down.
// Math.atan2(dy, dx).
// If we want "Tilt" from horizontal: abs(atan2(dy, dx))
export const calculateTilt = (p1: Point, p2: Point): number => {
if (!p1 || !p2) return 0;
const dy = p2.y - p1.y; // Y is down
const dx = p2.x - p1.x;
return degrees(Math.atan2(dy, dx));
};
// CVA: Angle between Horizontal and C7->Tragus
// C7 is pivot.
// Vector V = Tragus - C7.
// Angle = angle of V with Horizontal (-1, 0) (Posterior horizontal line)
export const calculateCVA = (c7: Point, tragus: Point): number => {
if (!c7 || !tragus) return 0;
// In image coords (Y down), Horizontal Posterior line from C7 (assuming facing Right) is (-1, 0).
// Wait, usually CVA is measured on Side view.
// If facing Right: Ear is to right of C7? No, Ear is above and slightly forward/back.
// CVA is formed by horizontal line at C7 and the line to the ear.
// Ideally it's ~50 degrees upwards from horizontal.
// Vector C7 -> Tragus
const dx = tragus.x - c7.x;
const dy = tragus.y - c7.y; // negative if ear is above C7
// We want angle against Horizontal.
// atan2(-dy, dx) gives angle in standard math coords (Y increasing up).
// Let's use simple atan2 of vector.
// If facing RIGHT: vector is (+x, -y). Angle ~ 45-60 deg.
// If facing LEFT: vector is (-x, -y). Angle ~ 120-135 deg?
// We want the inner angle relative to the horizontal line pointing BACKWARDS from the neck?
// Standard definition: Angle formed by horizontal line at C7 and line extending to Tragus.
// Usually measured from the horizontal looking forward? Or horizontal looking back?
// CVA < 50 is bad (head forward).
// If neck is perfectly vertical (impossible), angle is 90.
// If neck is horizontal (turtle), angle is 0.
// So it's angle from horizontal.
// We assume we want absolute angle from horizontal plane.
return Math.abs(degrees(Math.atan2(-dy, Math.abs(dx))));
};
// --- Landmark Estimations ---
export const estimateC7 = (shoulderL: Point, shoulderR: Point, nose: Point): Point => {
if (!shoulderL || !shoulderR || !nose) return { x: 0, y: 0 };
const midShoulder = {
x: (shoulderL.x + shoulderR.x) / 2,
y: (shoulderL.y + shoulderR.y) / 2
};
// Distance Nose to MidShoulder
const neckLen = dist(nose, midShoulder);
// Shift Up (negative Y) by 15%
// "przesunąć w górę o ok. 15% długości szyi"
// We assume C7 is roughly where the neck starts, MP shoulders are joint centers (lower).
// Actually C7 is slightly *above* the line connecting shoulder joints in 2D projection?
// Or is it? Acromion is high. C7 is often level or slightly above.
// Heuristic: Move UP.
return {
x: midShoulder.x,
y: midShoulder.y - (neckLen * 0.20) // 20% feels safer for C7 vs shoulder center line
};
};
export const correctASIS = (hipL: Point, hipR: Point, shoulderL: Point, shoulderR: Point): { l: Point, r: Point } => {
if (!hipL || !hipR || !shoulderL || !shoulderR) return { l: hipL, r: hipR };
const shoulderWidth = dist(shoulderL, shoulderR);
const offset = shoulderWidth * 0.15;
// Move Outwards
// Left Hip (on screen left usually) -> Move Left (-x)
// Right Hip -> Move Right (+x)
// Assuming Front view where Left Hip is on right side of image?
// MP: LEFT_HIP is person's left.
// If person faces cam: LEFT_HIP is on Image Right.
// So we need to check x coords to determine "Outwards".
// We assume MP Hips (23, 24) are internal.
// ASIS is lateral.
// Shift Outwards by 15% of Shoulder Width.
// Direction depend on which hip is left/right on SCREEN.
// Determine center of hips
const hipCenter = (hipL.x + hipR.x) / 2;
// HipL is usually on the Right side of the screen if Front View (Subject's Left).
// But MP documentation says:
// 23 - left hip
// 24 - right hip
// If user faces camera: 23 is on Screen Right (larger X), 24 is on Screen Left (smaller X).
// Let's just use the vector HipR -> HipL (Lateral direction).
// If Front View: 24(R) -> 23(L) is vector pointing RIGHT (positive X).
// If Back View: 23(L) -> 24(R) is vector pointing RIGHT.
// Safer: Move away from center.
const moveFromCenter = (pt: Point, centerX: number, amount: number) => {
if (pt.x < centerX) return { ...pt, x: pt.x - amount }; // Move Left
else return { ...pt, x: pt.x + amount }; // Move Right
};
const newL = moveFromCenter(hipL, hipCenter, offset);
const newR = moveFromCenter(hipR, hipCenter, offset);
return { l: newL, r: newR };
};
export const calculateTrunkLean = (midShoulder: Point, midHip: Point): number => {
// Angle against Vertical
if (!midShoulder || !midHip) return 0;
const dx = midShoulder.x - midHip.x;
const dy = midShoulder.y - midHip.y; // usually negative
// Vertical is 90.
const angle = degrees(Math.atan2(Math.abs(dy), Math.abs(dx))); // 90 is vertical
return Math.abs(90 - angle);
};
export const calculateATSI = (c7: Point, s1: Point, shoulderL: Point, shoulderR: Point, _hipL: Point, _hipR: Point): number => {
/*
ATSI (Anterior Trunk Symmetry Index) simplified:
Asymmetry of areas/distances.
Let's check lateral deviation of C7-S1 axis vs center of body blocks.
Or simpler: (Dist(C7, AcromionL) - Dist(C7, AcromionR)) / Mean...
User req: "różnice odległościowe między lewą a prawą stroną tułowia względem linii pośrodkowej"
*/
if (!c7 || !s1) return 0;
// Only feasible if we have solid Axis.
// Let's implement simple "Trunk Asymmetry":
// |( ShoulderL.x - Axis )| - |( ShoulderR.x - Axis )|
// Axis defined by S1 vertical? Or C7-S1 line.
// Let's approximate by Waist Triangle diff provided earlier, that's standard POTSI component.
// Here: return Asymmetry of Shoulders relative to C7 X-coord.
const distL = Math.abs(shoulderL.x - c7.x);
const distR = Math.abs(shoulderR.x - c7.x);
return Math.abs(distL - distR);
};