react-native-nitro-pose-exercises 1.1.5 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +207 -126
- package/lib/module/config/boat-pose.js +41 -0
- package/lib/module/config/boat-pose.js.map +1 -0
- package/lib/module/config/bow-pose.js +34 -0
- package/lib/module/config/bow-pose.js.map +1 -0
- package/lib/module/config/bridge-pose.js +34 -0
- package/lib/module/config/bridge-pose.js.map +1 -0
- package/lib/module/config/calf-raise.js +44 -0
- package/lib/module/config/calf-raise.js.map +1 -0
- package/lib/module/config/camel-pose.js +34 -0
- package/lib/module/config/camel-pose.js.map +1 -0
- package/lib/module/config/childs-pose.js +39 -0
- package/lib/module/config/childs-pose.js.map +1 -0
- package/lib/module/config/cobra-wings.js +44 -0
- package/lib/module/config/cobra-wings.js.map +1 -0
- package/lib/module/config/dead-lift.js +51 -0
- package/lib/module/config/dead-lift.js.map +1 -0
- package/lib/module/config/extended-side-angle.js +46 -0
- package/lib/module/config/extended-side-angle.js.map +1 -0
- package/lib/module/config/fish-pose.js +34 -0
- package/lib/module/config/fish-pose.js.map +1 -0
- package/lib/module/config/front-raise.js +51 -0
- package/lib/module/config/front-raise.js.map +1 -0
- package/lib/module/config/glute-bridge.js +46 -0
- package/lib/module/config/glute-bridge.js.map +1 -0
- package/lib/module/config/hip-abduction.js +39 -0
- package/lib/module/config/hip-abduction.js.map +1 -0
- package/lib/module/config/knee-raise.js +44 -0
- package/lib/module/config/knee-raise.js.map +1 -0
- package/lib/module/config/lateral-raise.js +51 -0
- package/lib/module/config/lateral-raise.js.map +1 -0
- package/lib/module/config/leg-raise.js +46 -0
- package/lib/module/config/leg-raise.js.map +1 -0
- package/lib/module/config/mountain-pose.js +46 -0
- package/lib/module/config/mountain-pose.js.map +1 -0
- package/lib/module/config/overarm-reach.js +44 -0
- package/lib/module/config/overarm-reach.js.map +1 -0
- package/lib/module/config/pull-up.js +44 -0
- package/lib/module/config/pull-up.js.map +1 -0
- package/lib/module/config/reverse-warrior.js +46 -0
- package/lib/module/config/reverse-warrior.js.map +1 -0
- package/lib/module/config/side-lung.js +51 -0
- package/lib/module/config/side-lung.js.map +1 -0
- package/lib/module/config/side-plank.js +34 -0
- package/lib/module/config/side-plank.js.map +1 -0
- package/lib/module/config/sumo-squat.js +51 -0
- package/lib/module/config/sumo-squat.js.map +1 -0
- package/lib/module/config/triangle-pose.js +46 -0
- package/lib/module/config/triangle-pose.js.map +1 -0
- package/lib/module/config/v-up.js +39 -0
- package/lib/module/config/v-up.js.map +1 -0
- package/lib/module/config/warrior-iii.js +58 -0
- package/lib/module/config/warrior-iii.js.map +1 -0
- package/lib/module/index.js +31 -3
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/config/boat-pose.d.ts +3 -0
- package/lib/typescript/src/config/boat-pose.d.ts.map +1 -0
- package/lib/typescript/src/config/bow-pose.d.ts +3 -0
- package/lib/typescript/src/config/bow-pose.d.ts.map +1 -0
- package/lib/typescript/src/config/bridge-pose.d.ts +3 -0
- package/lib/typescript/src/config/bridge-pose.d.ts.map +1 -0
- package/lib/typescript/src/config/calf-raise.d.ts +3 -0
- package/lib/typescript/src/config/calf-raise.d.ts.map +1 -0
- package/lib/typescript/src/config/camel-pose.d.ts +3 -0
- package/lib/typescript/src/config/camel-pose.d.ts.map +1 -0
- package/lib/typescript/src/config/childs-pose.d.ts +3 -0
- package/lib/typescript/src/config/childs-pose.d.ts.map +1 -0
- package/lib/typescript/src/config/cobra-wings.d.ts +3 -0
- package/lib/typescript/src/config/cobra-wings.d.ts.map +1 -0
- package/lib/typescript/src/config/dead-lift.d.ts +3 -0
- package/lib/typescript/src/config/dead-lift.d.ts.map +1 -0
- package/lib/typescript/src/config/extended-side-angle.d.ts +3 -0
- package/lib/typescript/src/config/extended-side-angle.d.ts.map +1 -0
- package/lib/typescript/src/config/fish-pose.d.ts +3 -0
- package/lib/typescript/src/config/fish-pose.d.ts.map +1 -0
- package/lib/typescript/src/config/front-raise.d.ts +3 -0
- package/lib/typescript/src/config/front-raise.d.ts.map +1 -0
- package/lib/typescript/src/config/glute-bridge.d.ts +3 -0
- package/lib/typescript/src/config/glute-bridge.d.ts.map +1 -0
- package/lib/typescript/src/config/hip-abduction.d.ts +3 -0
- package/lib/typescript/src/config/hip-abduction.d.ts.map +1 -0
- package/lib/typescript/src/config/knee-raise.d.ts +3 -0
- package/lib/typescript/src/config/knee-raise.d.ts.map +1 -0
- package/lib/typescript/src/config/lateral-raise.d.ts +3 -0
- package/lib/typescript/src/config/lateral-raise.d.ts.map +1 -0
- package/lib/typescript/src/config/leg-raise.d.ts +3 -0
- package/lib/typescript/src/config/leg-raise.d.ts.map +1 -0
- package/lib/typescript/src/config/mountain-pose.d.ts +3 -0
- package/lib/typescript/src/config/mountain-pose.d.ts.map +1 -0
- package/lib/typescript/src/config/overarm-reach.d.ts +3 -0
- package/lib/typescript/src/config/overarm-reach.d.ts.map +1 -0
- package/lib/typescript/src/config/pull-up.d.ts +3 -0
- package/lib/typescript/src/config/pull-up.d.ts.map +1 -0
- package/lib/typescript/src/config/reverse-warrior.d.ts +3 -0
- package/lib/typescript/src/config/reverse-warrior.d.ts.map +1 -0
- package/lib/typescript/src/config/side-lung.d.ts +3 -0
- package/lib/typescript/src/config/side-lung.d.ts.map +1 -0
- package/lib/typescript/src/config/side-plank.d.ts +3 -0
- package/lib/typescript/src/config/side-plank.d.ts.map +1 -0
- package/lib/typescript/src/config/sumo-squat.d.ts +3 -0
- package/lib/typescript/src/config/sumo-squat.d.ts.map +1 -0
- package/lib/typescript/src/config/triangle-pose.d.ts +3 -0
- package/lib/typescript/src/config/triangle-pose.d.ts.map +1 -0
- package/lib/typescript/src/config/v-up.d.ts +3 -0
- package/lib/typescript/src/config/v-up.d.ts.map +1 -0
- package/lib/typescript/src/config/warrior-iii.d.ts +3 -0
- package/lib/typescript/src/config/warrior-iii.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +26 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/config/boat-pose.ts +33 -0
- package/src/config/bow-pose.ts +24 -0
- package/src/config/bridge-pose.ts +25 -0
- package/src/config/calf-raise.ts +27 -0
- package/src/config/camel-pose.ts +24 -0
- package/src/config/childs-pose.ts +23 -0
- package/src/config/cobra-wings.ts +26 -0
- package/src/config/dead-lift.ts +35 -0
- package/src/config/extended-side-angle.ts +33 -0
- package/src/config/fish-pose.ts +25 -0
- package/src/config/front-raise.ts +54 -0
- package/src/config/glute-bridge.ts +33 -0
- package/src/config/hip-abduction.ts +25 -0
- package/src/config/knee-raise.ts +26 -0
- package/src/config/lateral-raise.ts +55 -0
- package/src/config/leg-raise.ts +33 -0
- package/src/config/mountain-pose.ts +33 -0
- package/src/config/overarm-reach.ts +46 -0
- package/src/config/pull-up.ts +27 -0
- package/src/config/reverse-warrior.ts +33 -0
- package/src/config/side-lung.ts +34 -0
- package/src/config/side-plank.ts +25 -0
- package/src/config/sumo-squat.ts +34 -0
- package/src/config/triangle-pose.ts +34 -0
- package/src/config/v-up.ts +25 -0
- package/src/config/warrior-iii.ts +42 -0
- package/src/index.tsx +31 -3
package/README.md
CHANGED
|
@@ -8,12 +8,14 @@
|
|
|
8
8
|
|
|
9
9
|
A **React Native Nitro Module** for real-time, on-device exercise tracking using pose estimation. Uses **OS-native pose detection** — Apple Vision on iOS and Google ML Kit on Android — with **VisionCamera v5**.
|
|
10
10
|
|
|
11
|
-
* 🏋️ **
|
|
11
|
+
* 🏋️ **38 Built-In Exercises** — Push-ups, squats, deadlifts, yoga poses, and more
|
|
12
|
+
* 🔄 **Rep Counting** — Automatic rep detection with configurable state machines
|
|
12
13
|
* 🧘 **Hold Tracking** — Duration and stability tracking for planks, yoga poses, and isometric holds
|
|
13
|
-
* 📐 **Form Validation** — Real-time form feedback with
|
|
14
|
-
* 💀 **Skeleton Overlay** —
|
|
14
|
+
* 📐 **Form Validation** — Real-time form feedback with angle-based rules
|
|
15
|
+
* 💀 **Skeleton Overlay** — Skia-powered skeleton with glow effects and live angle badges
|
|
15
16
|
* ⚡ **Fully Native** — OS-level pose detection via Nitro Modules, zero JS bridge overhead
|
|
16
17
|
* 📦 **Zero Model Bundling** — No ML model files to download or ship with your app
|
|
18
|
+
* 🪶 **~200 KB** — Virtually zero app size impact
|
|
17
19
|
|
|
18
20
|
---
|
|
19
21
|
|
|
@@ -56,15 +58,15 @@ cd ios && pod install
|
|
|
56
58
|
|
|
57
59
|
<table>
|
|
58
60
|
<tr>
|
|
59
|
-
<th align="center"
|
|
60
|
-
<th align="center"
|
|
61
|
+
<th align="center">📸 Normal Mode</th>
|
|
62
|
+
<th align="center">💀 Skeleton + Angle Overlay</th>
|
|
61
63
|
</tr>
|
|
62
64
|
<tr>
|
|
63
65
|
<td align="center">
|
|
64
|
-
|
|
66
|
+
<img alt="normal-mode" src="./docs/img/normal.png" height="650" width="300"/>
|
|
65
67
|
</td>
|
|
66
68
|
<td align="center">
|
|
67
|
-
|
|
69
|
+
<img alt="skeleton-mode" src="./docs/img/skeleton.png" height="650" width="300"/>
|
|
68
70
|
</td>
|
|
69
71
|
</tr>
|
|
70
72
|
</table>
|
|
@@ -75,12 +77,12 @@ cd ios && pod install
|
|
|
75
77
|
|
|
76
78
|
| Feature | Description |
|
|
77
79
|
| --- | --- |
|
|
78
|
-
| **Rep-Based Exercises** | Cyclic state machine (UP → DOWN → UP = 1 rep). Push-ups, squats, curls. |
|
|
79
|
-
| **Hold-Based Exercises** | Single target pose with duration tracking. Planks, wall sits, yoga poses. |
|
|
80
|
-
| **
|
|
81
|
-
| **
|
|
82
|
-
| **Skeleton Overlay** | Body skeleton drawn over camera via Skia (19 joints iOS, 33 joints Android). |
|
|
80
|
+
| **Rep-Based Exercises** | Cyclic state machine (UP → DOWN → UP = 1 rep). Push-ups, squats, curls, and more. |
|
|
81
|
+
| **Hold-Based Exercises** | Single target pose with duration + stability tracking. Planks, wall sits, yoga poses. |
|
|
82
|
+
| **Form Feedback** | Angle-based rules with throttled real-time callbacks. Bad form blocks rep counting. |
|
|
83
|
+
| **Skeleton Overlay** | Glow-effect bones, color-coded joints, and live angle badges drawn over camera via Skia. |
|
|
83
84
|
| **Bilateral Tracking** | Left and right side angles tracked independently. |
|
|
85
|
+
| **Fatigue Guard** | Minimum 800ms per rep prevents false counts. Form score gate rejects bad reps. |
|
|
84
86
|
|
|
85
87
|
---
|
|
86
88
|
|
|
@@ -90,8 +92,8 @@ cd ios && pod install
|
|
|
90
92
|
|
|
91
93
|
Unlike MediaPipe-based solutions, this library uses OS-native APIs. There is **no model file to download or bundle**.
|
|
92
94
|
|
|
93
|
-
* **iOS:** Apple Vision is a system framework —
|
|
94
|
-
* **Android:** ML Kit manages its own model via Google Play Services —
|
|
95
|
+
* **iOS:** Apple Vision is a system framework — already on every iPhone running iOS 14+.
|
|
96
|
+
* **Android:** ML Kit manages its own model via Google Play Services — downloads and updates automatically.
|
|
95
97
|
|
|
96
98
|
### Permissions
|
|
97
99
|
|
|
@@ -124,18 +126,14 @@ module.exports = {
|
|
|
124
126
|
|
|
125
127
|
### Podspec (for library authors)
|
|
126
128
|
|
|
127
|
-
The iOS podspec needs the Vision and AVFoundation system frameworks:
|
|
128
|
-
|
|
129
129
|
```ruby
|
|
130
|
-
|
|
130
|
+
s.frameworks = ['Vision', 'AVFoundation']
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
No CocoaPods dependencies required — Vision is built into iOS.
|
|
134
134
|
|
|
135
135
|
### Android Gradle (for library authors)
|
|
136
136
|
|
|
137
|
-
Add ML Kit Pose Detection to `android/build.gradle`:
|
|
138
|
-
|
|
139
137
|
```groovy
|
|
140
138
|
dependencies {
|
|
141
139
|
implementation 'com.google.mlkit:pose-detection:18.0.0-beta5'
|
|
@@ -149,8 +147,8 @@ dependencies {
|
|
|
149
147
|
### Basic — Normal Camera (No Skeleton)
|
|
150
148
|
|
|
151
149
|
```tsx
|
|
152
|
-
import { useEffect,
|
|
153
|
-
import { StyleSheet, View, Text
|
|
150
|
+
import { useEffect, useState } from 'react';
|
|
151
|
+
import { StyleSheet, View, Text } from 'react-native';
|
|
154
152
|
import {
|
|
155
153
|
Camera,
|
|
156
154
|
useCameraDevice,
|
|
@@ -162,7 +160,6 @@ import {
|
|
|
162
160
|
nitroPoseExercises,
|
|
163
161
|
PUSHUP_CONFIG,
|
|
164
162
|
type RepData,
|
|
165
|
-
type FormFeedback,
|
|
166
163
|
type SessionResult,
|
|
167
164
|
} from 'react-native-nitro-pose-exercises';
|
|
168
165
|
|
|
@@ -176,7 +173,6 @@ export default function App() {
|
|
|
176
173
|
if (!hasPermission) requestPermission();
|
|
177
174
|
}, [hasPermission]);
|
|
178
175
|
|
|
179
|
-
// Initialize pose engine — modelPath is ignored (OS-native, no model file)
|
|
180
176
|
useEffect(() => {
|
|
181
177
|
async function init() {
|
|
182
178
|
await nitroPoseExercises.initialize('');
|
|
@@ -184,30 +180,19 @@ export default function App() {
|
|
|
184
180
|
|
|
185
181
|
nitroPoseExercises.onRepComplete = (data: RepData) => {
|
|
186
182
|
setRepCount(data.repNumber);
|
|
187
|
-
console.log(`Rep ${data.repNumber} — form: ${data.formScore}`);
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
nitroPoseExercises.onFormFeedback = (feedback: FormFeedback) => {
|
|
191
|
-
console.log(`Form: ${feedback.message}`);
|
|
192
183
|
};
|
|
193
184
|
|
|
194
185
|
nitroPoseExercises.onSessionComplete = (result: SessionResult) => {
|
|
195
|
-
console.log(
|
|
196
|
-
`Done! ${result.totalReps} reps, avg form: ${result.averageFormScore}`
|
|
197
|
-
);
|
|
186
|
+
console.log(`Done! ${result.totalReps} reps, form: ${result.averageFormScore}`);
|
|
198
187
|
};
|
|
199
188
|
|
|
200
|
-
// Start: 10 target reps, 3 second countdown
|
|
201
189
|
nitroPoseExercises.startSession(10, 3);
|
|
202
190
|
}
|
|
203
191
|
|
|
204
192
|
init();
|
|
205
|
-
return () => {
|
|
206
|
-
nitroPoseExercises.release();
|
|
207
|
-
};
|
|
193
|
+
return () => { nitroPoseExercises.release(); };
|
|
208
194
|
}, []);
|
|
209
195
|
|
|
210
|
-
// Frame processor
|
|
211
196
|
const frameOutput = useFrameOutput({
|
|
212
197
|
pixelFormat: 'rgb',
|
|
213
198
|
onFrame(frame) {
|
|
@@ -251,20 +236,55 @@ const styles = StyleSheet.create({
|
|
|
251
236
|
});
|
|
252
237
|
```
|
|
253
238
|
|
|
254
|
-
### Skeleton Overlay — SkiaCamera
|
|
239
|
+
### Skeleton Overlay with Angle Badges — SkiaCamera
|
|
240
|
+
|
|
241
|
+
> **Critical:** Create all `Skia.Paint()` and `Skia.Font()` objects **outside** the `onFrame` callback. Creating them inside causes memory leaks and crashes within seconds.
|
|
255
242
|
|
|
256
243
|
```tsx
|
|
257
|
-
import {
|
|
258
|
-
import {
|
|
259
|
-
import { nitroPoseExercises } from 'react-native-nitro-pose-exercises'
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
244
|
+
import { Skia } from '@shopify/react-native-skia';
|
|
245
|
+
import { SkiaCamera } from 'react-native-vision-camera-skia';
|
|
246
|
+
import { nitroPoseExercises } from 'react-native-nitro-pose-exercises';
|
|
247
|
+
|
|
248
|
+
// Create paints ONCE at module level — NEVER inside onFrame
|
|
249
|
+
const GLOW_PAINT = Skia.Paint();
|
|
250
|
+
GLOW_PAINT.setColor(Skia.Color('rgba(0, 255, 102, 0.3)'));
|
|
251
|
+
GLOW_PAINT.setStrokeWidth(14);
|
|
252
|
+
GLOW_PAINT.setStyle(1);
|
|
253
|
+
GLOW_PAINT.setStrokeCap(1);
|
|
254
|
+
GLOW_PAINT.setAntiAlias(true);
|
|
255
|
+
|
|
256
|
+
const BONE_PAINT = Skia.Paint();
|
|
257
|
+
BONE_PAINT.setColor(Skia.Color('#00FF66'));
|
|
258
|
+
BONE_PAINT.setStrokeWidth(6);
|
|
259
|
+
BONE_PAINT.setStyle(1);
|
|
260
|
+
BONE_PAINT.setStrokeCap(1);
|
|
261
|
+
BONE_PAINT.setAntiAlias(true);
|
|
262
|
+
|
|
263
|
+
const JOINT_PAINT = Skia.Paint();
|
|
264
|
+
JOINT_PAINT.setColor(Skia.Color('#FF3366'));
|
|
265
|
+
JOINT_PAINT.setStyle(0);
|
|
266
|
+
JOINT_PAINT.setAntiAlias(true);
|
|
267
|
+
|
|
268
|
+
const KEY_JOINT_PAINT = Skia.Paint();
|
|
269
|
+
KEY_JOINT_PAINT.setColor(Skia.Color('#00FFFF'));
|
|
270
|
+
KEY_JOINT_PAINT.setStyle(0);
|
|
271
|
+
KEY_JOINT_PAINT.setAntiAlias(true);
|
|
272
|
+
|
|
273
|
+
const ANGLE_BG_PAINT = Skia.Paint();
|
|
274
|
+
ANGLE_BG_PAINT.setColor(Skia.Color('rgba(0, 0, 0, 0.7)'));
|
|
275
|
+
ANGLE_BG_PAINT.setStyle(0);
|
|
276
|
+
|
|
277
|
+
const ANGLE_TEXT_FONT = Skia.Font(null, 14);
|
|
278
|
+
const ANGLE_TEXT_PAINT = Skia.Paint();
|
|
279
|
+
ANGLE_TEXT_PAINT.setColor(Skia.Color('#FFFFFF'));
|
|
280
|
+
|
|
281
|
+
const SKELETON_CONNECTIONS = [
|
|
282
|
+
[11, 12], [11, 23], [12, 24], [23, 24],
|
|
283
|
+
[11, 13], [13, 15], [12, 14], [14, 16],
|
|
284
|
+
[23, 25], [25, 27], [24, 26], [26, 28],
|
|
285
|
+
];
|
|
286
|
+
|
|
287
|
+
const KEY_LANDMARKS = [11, 12, 13, 14, 15, 16, 23, 24, 25, 26, 27, 28];
|
|
268
288
|
|
|
269
289
|
<SkiaCamera
|
|
270
290
|
style={StyleSheet.absoluteFill}
|
|
@@ -272,49 +292,71 @@ const SKELETON_CONNECTIONS: [number, number][] = [
|
|
|
272
292
|
device="back"
|
|
273
293
|
pixelFormat="rgb"
|
|
274
294
|
onFrame={(frame, render) => {
|
|
275
|
-
'worklet'
|
|
295
|
+
'worklet';
|
|
276
296
|
try {
|
|
277
|
-
nitroPoseExercises.processFrame(frame)
|
|
278
|
-
const landmarks = nitroPoseExercises.landmarks
|
|
297
|
+
nitroPoseExercises.processFrame(frame);
|
|
298
|
+
const landmarks = nitroPoseExercises.landmarks;
|
|
279
299
|
|
|
280
300
|
render(({ frameTexture, canvas }) => {
|
|
281
|
-
canvas.drawImage(frameTexture, 0, 0)
|
|
301
|
+
canvas.drawImage(frameTexture, 0, 0);
|
|
282
302
|
|
|
283
303
|
if (landmarks && landmarks.length > 0) {
|
|
284
|
-
const w = frame.width
|
|
285
|
-
const h = frame.height
|
|
286
|
-
|
|
287
|
-
// Draw bones
|
|
288
|
-
const linePaint = Skia.Paint()
|
|
289
|
-
linePaint.setColor(Skia.Color('#00FF00'))
|
|
290
|
-
linePaint.setStrokeWidth(4)
|
|
291
|
-
linePaint.setStyle(1)
|
|
304
|
+
const w = frame.width;
|
|
305
|
+
const h = frame.height;
|
|
292
306
|
|
|
307
|
+
// Glow layer (thick translucent)
|
|
293
308
|
for (const [i, j] of SKELETON_CONNECTIONS) {
|
|
294
309
|
if (i < landmarks.length && j < landmarks.length) {
|
|
295
|
-
const a = landmarks[i]
|
|
296
|
-
const b = landmarks[j]
|
|
310
|
+
const a = landmarks[i];
|
|
311
|
+
const b = landmarks[j];
|
|
297
312
|
if (a.visibility > 0.5 && b.visibility > 0.5) {
|
|
298
|
-
canvas.drawLine(a.x * w, a.y * h, b.x * w, b.y * h,
|
|
313
|
+
canvas.drawLine(a.x * w, a.y * h, b.x * w, b.y * h, GLOW_PAINT);
|
|
299
314
|
}
|
|
300
315
|
}
|
|
301
316
|
}
|
|
302
317
|
|
|
303
|
-
//
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
318
|
+
// Solid bones on top
|
|
319
|
+
for (const [i, j] of SKELETON_CONNECTIONS) {
|
|
320
|
+
if (i < landmarks.length && j < landmarks.length) {
|
|
321
|
+
const a = landmarks[i];
|
|
322
|
+
const b = landmarks[j];
|
|
323
|
+
if (a.visibility > 0.5 && b.visibility > 0.5) {
|
|
324
|
+
canvas.drawLine(a.x * w, a.y * h, b.x * w, b.y * h, BONE_PAINT);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
307
328
|
|
|
329
|
+
// Joints with glow rings
|
|
308
330
|
for (let idx = 0; idx < landmarks.length; idx++) {
|
|
309
|
-
const lm = landmarks[idx]
|
|
310
|
-
if (lm.visibility > 0.5) {
|
|
311
|
-
|
|
331
|
+
const lm = landmarks[idx];
|
|
332
|
+
if (lm && lm.visibility > 0.5) {
|
|
333
|
+
const isKey = KEY_LANDMARKS.includes(idx);
|
|
334
|
+
if (isKey) canvas.drawCircle(lm.x * w, lm.y * h, 12, GLOW_PAINT);
|
|
335
|
+
canvas.drawCircle(lm.x * w, lm.y * h, isKey ? 8 : 4, isKey ? KEY_JOINT_PAINT : JOINT_PAINT);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Elbow angle badges
|
|
340
|
+
for (const [shoulderIdx, elbowIdx, wristIdx] of [[11, 13, 15], [12, 14, 16]]) {
|
|
341
|
+
const s = landmarks[shoulderIdx];
|
|
342
|
+
const e = landmarks[elbowIdx];
|
|
343
|
+
const wr = landmarks[wristIdx];
|
|
344
|
+
if (s?.visibility > 0.5 && e?.visibility > 0.5 && wr?.visibility > 0.5) {
|
|
345
|
+
const vaX = s.x - e.x, vaY = s.y - e.y;
|
|
346
|
+
const vcX = wr.x - e.x, vcY = wr.y - e.y;
|
|
347
|
+
const dot = vaX * vcX + vaY * vcY;
|
|
348
|
+
const mag = Math.sqrt(vaX * vaX + vaY * vaY) * Math.sqrt(vcX * vcX + vcY * vcY);
|
|
349
|
+
const angle = Math.round(Math.acos(Math.max(-1, Math.min(1, dot / mag))) * (180 / Math.PI));
|
|
350
|
+
const tx = e.x * w + 15;
|
|
351
|
+
const ty = e.y * h - 10;
|
|
352
|
+
canvas.drawRoundRect({ x: tx - 4, y: ty - 14, width: 48, height: 20 }, 6, 6, ANGLE_BG_PAINT);
|
|
353
|
+
canvas.drawText(`${angle}°`, tx, ty, ANGLE_TEXT_PAINT, ANGLE_TEXT_FONT);
|
|
312
354
|
}
|
|
313
355
|
}
|
|
314
356
|
}
|
|
315
|
-
})
|
|
357
|
+
});
|
|
316
358
|
} finally {
|
|
317
|
-
frame.dispose()
|
|
359
|
+
frame.dispose();
|
|
318
360
|
}
|
|
319
361
|
}}
|
|
320
362
|
/>
|
|
@@ -337,7 +379,6 @@ release(): void
|
|
|
337
379
|
### Exercise Setup
|
|
338
380
|
|
|
339
381
|
```ts
|
|
340
|
-
// Load an exercise config (built-in or custom)
|
|
341
382
|
loadExercise(config: ExerciseConfig): void
|
|
342
383
|
```
|
|
343
384
|
|
|
@@ -353,7 +394,7 @@ stopSession(): void
|
|
|
353
394
|
### Frame Processing
|
|
354
395
|
|
|
355
396
|
```ts
|
|
356
|
-
//
|
|
397
|
+
// Call from VisionCamera frame processor worklet
|
|
357
398
|
processFrame(frame: Frame): void
|
|
358
399
|
```
|
|
359
400
|
|
|
@@ -363,7 +404,7 @@ processFrame(frame: Frame): void
|
|
|
363
404
|
readonly status: SessionStatus // 'idle' | 'countdown' | 'active' | 'paused' | 'completed'
|
|
364
405
|
readonly currentPhase: ExercisePhase // 'up' | 'down' | 'hold' | 'transition' | 'unknown'
|
|
365
406
|
readonly repCount: number
|
|
366
|
-
readonly landmarks: Landmark[] // Body landmarks
|
|
407
|
+
readonly landmarks: Landmark[] // Body landmarks mapped to MediaPipe indices
|
|
367
408
|
```
|
|
368
409
|
|
|
369
410
|
### Callbacks
|
|
@@ -378,18 +419,16 @@ onPoseRegained: (() => void) | undefined
|
|
|
378
419
|
onSessionComplete: ((result: SessionResult) => void) | undefined
|
|
379
420
|
```
|
|
380
421
|
|
|
381
|
-
---
|
|
382
|
-
|
|
383
422
|
### Callback Payloads
|
|
384
423
|
|
|
385
424
|
#### RepData
|
|
386
425
|
|
|
387
426
|
```ts
|
|
388
427
|
{
|
|
389
|
-
repNumber: number
|
|
390
|
-
durationMs: number
|
|
391
|
-
formScore: number
|
|
392
|
-
angles: AngleSnapshot[]
|
|
428
|
+
repNumber: number
|
|
429
|
+
durationMs: number
|
|
430
|
+
formScore: number // 0-100
|
|
431
|
+
angles: AngleSnapshot[] // all tracked angles at rep completion
|
|
393
432
|
}
|
|
394
433
|
```
|
|
395
434
|
|
|
@@ -397,9 +436,9 @@ onSessionComplete: ((result: SessionResult) => void) | undefined
|
|
|
397
436
|
|
|
398
437
|
```ts
|
|
399
438
|
{
|
|
400
|
-
ruleName: string
|
|
401
|
-
message: string
|
|
402
|
-
severity: FormSeverity
|
|
439
|
+
ruleName: string
|
|
440
|
+
message: string
|
|
441
|
+
severity: FormSeverity // 'info' | 'warning' | 'error'
|
|
403
442
|
}
|
|
404
443
|
```
|
|
405
444
|
|
|
@@ -418,37 +457,68 @@ onSessionComplete: ((result: SessionResult) => void) | undefined
|
|
|
418
457
|
|
|
419
458
|
---
|
|
420
459
|
|
|
421
|
-
## 🏋️ Built-In Exercise Configs
|
|
460
|
+
## 🏋️ All 38 Built-In Exercise Configs
|
|
461
|
+
|
|
462
|
+
### Rep-Based: Strength (15 exercises)
|
|
463
|
+
|
|
464
|
+
| Config | Exercise | Primary Angle | Camera View |
|
|
465
|
+
| --- | --- | --- | --- |
|
|
466
|
+
| `PUSHUP_CONFIG` | Push-Up | Elbow 140°–180° / 30°–110° | Side |
|
|
467
|
+
| `PULL_UP_CONFIG` | Pull-Up | Elbow 150°–180° / 40°–90° | Side |
|
|
468
|
+
| `SQUAT_CONFIG` | Squat | Knee 155°–180° / 50°–105° | Side |
|
|
469
|
+
| `SUMO_SQUAT_CONFIG` | Sumo Squat | Knee 155°–180° / 60°–110° | Front |
|
|
470
|
+
| `BICEP_CURL_CONFIG` | Bicep Curl | Elbow 150°–180° / 25°–70° | Side |
|
|
471
|
+
| `SHOULDER_PRESS_CONFIG` | Shoulder Press | Elbow 155°–180° / 60°–100° | Side |
|
|
472
|
+
| `LUNGE_CONFIG` | Lunge | Front knee 155°–180° / 70°–110° | Side |
|
|
473
|
+
| `SIDE_LUNGE_CONFIG` | Side Lunge | Bent knee 155°–180° / 70°–110° | Front |
|
|
474
|
+
| `TRICEP_DIP_CONFIG` | Tricep Dip | Elbow 150°–180° / 60°–100° | Side |
|
|
475
|
+
| `DEADLIFT_CONFIG` | Deadlift | Hip 160°–180° / 60°–120° | Side |
|
|
476
|
+
| `LATERAL_RAISE_CONFIG` | Lateral Raise | Shoulder abduction 5°–30° / 75°–110° | Front |
|
|
477
|
+
| `FRONT_RAISE_CONFIG` | Front Raise | Shoulder flexion 0°–25° / 75°–110° | Side |
|
|
478
|
+
| `CALF_RAISE_CONFIG` | Calf Raise | Ankle 70°–95° / 110°–150° | Side |
|
|
479
|
+
| `OVERARM_REACH_CONFIG` | Overarm Reach | Shoulder abduction 0°–30° / 155°–180° | Front |
|
|
480
|
+
| `HIP_ABDUCTION_CONFIG` | Hip Abduction | Leg spread 0°–15° / 30°–60° | Front |
|
|
481
|
+
|
|
482
|
+
### Rep-Based: Core (6 exercises)
|
|
483
|
+
|
|
484
|
+
| Config | Exercise | Primary Angle | Camera View |
|
|
485
|
+
| --- | --- | --- | --- |
|
|
486
|
+
| `SITUP_CONFIG` | Sit-Up | Hip 130°–180° / 40°–90° | Side |
|
|
487
|
+
| `LEG_RAISE_CONFIG` | Leg Raise | Hip 150°–180° / 60°–110° | Side |
|
|
488
|
+
| `V_UP_CONFIG` | V-Up | Hip fold 150°–180° / 30°–80° | Side |
|
|
489
|
+
| `GLUTE_BRIDGE_CONFIG` | Glute Bridge | Hip extension 80°–120° / 155°–180° | Side |
|
|
490
|
+
| `COBRA_WINGS_CONFIG` | Cobra Wings | Hip extension 160°–180° / 120°–155° | Side |
|
|
491
|
+
| `KNEE_RAISE_CONFIG` | Knee Raise | Hip 155°–180° / 60°–110° | Side |
|
|
492
|
+
|
|
493
|
+
### Hold-Based: Strength (3 exercises)
|
|
494
|
+
|
|
495
|
+
| Config | Exercise | Hold Angle | Default Duration |
|
|
496
|
+
| --- | --- | --- | --- |
|
|
497
|
+
| `PLANK_CONFIG` | Plank | Hip 155°–180° | 60s |
|
|
498
|
+
| `SIDE_PLANK_CONFIG` | Side Plank | Hip lateral 155°–180° | 30s |
|
|
499
|
+
| `WALL_SIT_CONFIG` | Wall Sit | Knee 80°–110° | 45s |
|
|
422
500
|
|
|
423
|
-
###
|
|
501
|
+
### Hold-Based: Yoga (14 exercises)
|
|
424
502
|
|
|
425
|
-
| Config | Exercise |
|
|
503
|
+
| Config | Exercise | Hold Angle | Default Duration |
|
|
426
504
|
| --- | --- | --- | --- |
|
|
427
|
-
| `
|
|
428
|
-
| `
|
|
429
|
-
| `
|
|
430
|
-
| `
|
|
431
|
-
| `
|
|
432
|
-
| `
|
|
433
|
-
| `
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
|
438
|
-
|
|
|
439
|
-
| `
|
|
440
|
-
| `
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
| Config | Exercise | Hold Angle | Duration | Form Rules |
|
|
445
|
-
| --- | --- | --- | --- | --- |
|
|
446
|
-
| `TREE_POSE_CONFIG` | Tree Pose (Vrksasana) | Standing leg 165°–180° | 30s | Standing leg bent, leaning torso |
|
|
447
|
-
| `WARRIOR_I_CONFIG` | Warrior I (Virabhadrasana I) | Front knee 80°–110° | 30s | Knee too straight, back leg bent, arms not extended, torso leaning |
|
|
448
|
-
| `WARRIOR_II_CONFIG` | Warrior II (Virabhadrasana II) | Front knee 80°–110° | 30s | Knee too straight, back leg bent, arms drooping |
|
|
449
|
-
| `DOWNWARD_DOG_CONFIG` | Downward Dog (Adho Mukha Svanasana) | Hip 55°–100° | 30s | Arms bent, legs bent, hips too low |
|
|
450
|
-
| `CHAIR_POSE_CONFIG` | Chair Pose (Utkatasana) | Knee 90°–130° | 30s | Knees too straight, leaning forward, arms not up |
|
|
451
|
-
| `COBRA_POSE_CONFIG` | Cobra Pose (Bhujangasana) | Hip extension 120°–170° | 30s | Shoulders tensed, legs bending |
|
|
505
|
+
| `MOUNTAIN_POSE_CONFIG` | Mountain Pose (Tadasana) | Knee 170°–180° | 30s |
|
|
506
|
+
| `TREE_POSE_CONFIG` | Tree Pose (Vrksasana) | Standing leg 165°–180° | 30s |
|
|
507
|
+
| `CHAIR_POSE_CONFIG` | Chair Pose (Utkatasana) | Knee 90°–130° | 30s |
|
|
508
|
+
| `WARRIOR_I_CONFIG` | Warrior I (Virabhadrasana I) | Front knee 80°–110° | 30s |
|
|
509
|
+
| `WARRIOR_II_CONFIG` | Warrior II (Virabhadrasana II) | Front knee 80°–110° | 30s |
|
|
510
|
+
| `WARRIOR_III_CONFIG` | Warrior III (Virabhadrasana III) | Hip hinge 70°–110° | 30s |
|
|
511
|
+
| `REVERSE_WARRIOR_CONFIG` | Reverse Warrior | Front knee 80°–110° | 30s |
|
|
512
|
+
| `DOWNWARD_DOG_CONFIG` | Downward Dog (Adho Mukha Svanasana) | Hip 55°–100° | 30s |
|
|
513
|
+
| `COBRA_POSE_CONFIG` | Cobra Pose (Bhujangasana) | Hip extension 120°–170° | 30s |
|
|
514
|
+
| `TRIANGLE_POSE_CONFIG` | Triangle Pose (Trikonasana) | Front leg 160°–180° | 30s |
|
|
515
|
+
| `EXTENDED_SIDE_ANGLE_CONFIG` | Extended Side Angle (Utthita Parsvakonasana) | Front knee 80°–110° | 30s |
|
|
516
|
+
| `BRIDGE_POSE_CONFIG` | Bridge Pose (Setu Bandhasana) | Knee 80°–110° | 30s |
|
|
517
|
+
| `BOAT_POSE_CONFIG` | Boat Pose (Navasana) | Hip flexion 60°–110° | 30s |
|
|
518
|
+
| `CAMEL_POSE_CONFIG` | Camel Pose (Ustrasana) | Hip extension 120°–165° | 30s |
|
|
519
|
+
| `CHILDS_POSE_CONFIG` | Child's Pose (Balasana) | Hip fold 30°–80° | 60s |
|
|
520
|
+
| `BOW_POSE_CONFIG` | Bow Pose (Dhanurasana) | Knee 50°–100° | 30s |
|
|
521
|
+
| `FISH_POSE_CONFIG` | Fish Pose (Matsyasana) | Chest open 130°–170° | 30s |
|
|
452
522
|
|
|
453
523
|
### Custom Exercise Config
|
|
454
524
|
|
|
@@ -486,12 +556,24 @@ Landmarks are mapped to MediaPipe-compatible indices on both platforms. iOS Visi
|
|
|
486
556
|
| 14 | Right elbow | 26 | Right knee |
|
|
487
557
|
| 15 | Left wrist | 27 | Left ankle |
|
|
488
558
|
|
|
489
|
-
**iOS note:** Vision provides 19 joints. Indices not available
|
|
559
|
+
**iOS note:** Vision provides 19 joints. Indices not available (face 1-10, hands 17-22, feet 29-32) are filled with `visibility: 0`.
|
|
490
560
|
|
|
491
561
|
**Android note:** ML Kit provides all 33 landmarks matching MediaPipe indices exactly.
|
|
492
562
|
|
|
493
563
|
---
|
|
494
564
|
|
|
565
|
+
## 📏 Camera Angle Guide
|
|
566
|
+
|
|
567
|
+
| ✅ Good | ❌ Bad |
|
|
568
|
+
| --- | --- |
|
|
569
|
+
| Side view, full body visible | Front-facing view |
|
|
570
|
+
| Phone at waist height, 6-8 ft away | Ground-level angle |
|
|
571
|
+
| Well-lit environment | Heavy glare or backlight |
|
|
572
|
+
|
|
573
|
+
Each exercise config includes a `cameraAngle` recommendation (`'side'` or `'front'`). Side view works for most exercises. Front view is needed for lateral raises, sumo squats, warrior II, and hip abductions.
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
495
577
|
## 🏗️ Architecture — OS-Native vs MediaPipe
|
|
496
578
|
|
|
497
579
|
| | OS-Native (current) | MediaPipe (previous) |
|
|
@@ -499,21 +581,22 @@ Landmarks are mapped to MediaPipe-compatible indices on both platforms. iOS Visi
|
|
|
499
581
|
| **iOS** | Apple Vision framework (built-in) | MediaPipeTasksVision (CocoaPod) |
|
|
500
582
|
| **Android** | Google ML Kit (Play Services) | com.google.mediapipe:tasks-vision |
|
|
501
583
|
| **Model file** | None needed | ~3 MB bundled `.task` file |
|
|
502
|
-
| **Color conversion** | None — takes CVPixelBuffer/
|
|
584
|
+
| **Color conversion** | None — takes CVPixelBuffer/InputImage directly | BGRA required (iOS), NV21→RGB (Android) |
|
|
503
585
|
| **App size impact** | ~200 KB (Nitro module code only) | ~11-15 MB (SDK + model) |
|
|
504
586
|
| **Updates** | OS/Play Services updates | Manual model file replacement |
|
|
505
587
|
|
|
506
588
|
---
|
|
507
589
|
|
|
508
|
-
##
|
|
509
|
-
|
|
510
|
-
For best results, the camera should see the exerciser from a **side profile**:
|
|
590
|
+
## 🛡️ Safety Features
|
|
511
591
|
|
|
512
|
-
|
|
|
592
|
+
| Feature | Description |
|
|
513
593
|
| --- | --- |
|
|
514
|
-
|
|
|
515
|
-
|
|
|
516
|
-
|
|
|
594
|
+
| **Min rep duration** | 800ms minimum per rep — prevents false counts from sensor noise |
|
|
595
|
+
| **Form score gate** | Reps with form score below 30/100 are rejected and not counted |
|
|
596
|
+
| **Feedback throttle** | Same form warning fires max once every 5 seconds to avoid UI spam |
|
|
597
|
+
| **Pose lost detection** | `onPoseLost` / `onPoseRegained` callbacks when user exits/enters frame |
|
|
598
|
+
| **Frame throttle** | Processes every 3rd frame to reduce CPU load without losing accuracy |
|
|
599
|
+
| **Visibility filter** | Landmarks with confidence below 0.3 are excluded from angle calculations |
|
|
517
600
|
|
|
518
601
|
---
|
|
519
602
|
|
|
@@ -521,8 +604,8 @@ For best results, the camera should see the exerciser from a **side profile**:
|
|
|
521
604
|
|
|
522
605
|
| Platform | Status | Notes |
|
|
523
606
|
| --- | --- | --- |
|
|
524
|
-
| **iOS** | ✅ Supported |
|
|
525
|
-
| **Android** | ✅ Supported | API 23
|
|
607
|
+
| **iOS** | ✅ Supported | Physical device, iOS 14+ (Vision body pose) |
|
|
608
|
+
| **Android** | ✅ Supported | API 23+, Google Play Services required |
|
|
526
609
|
| **iOS Simulator** | ❌ Not supported | No camera access |
|
|
527
610
|
| **Android Emulator** | ❌ Not supported | No real camera feed |
|
|
528
611
|
|
|
@@ -537,13 +620,11 @@ For best results, the camera should see the exerciser from a **side profile**:
|
|
|
537
620
|
| Vision framework (iOS, built-in) | ~0 KB (system framework) |
|
|
538
621
|
| **Total new addition** | **~200 KB** |
|
|
539
622
|
|
|
540
|
-
Compared to the MediaPipe approach (~11-15 MB), the OS-native approach adds virtually zero app size.
|
|
541
|
-
|
|
542
623
|
---
|
|
543
624
|
|
|
544
625
|
## 🤝 Contributing
|
|
545
626
|
|
|
546
|
-
PRs welcome!
|
|
627
|
+
PRs welcome! Adding a new exercise is as simple as creating a config file — no native code changes needed.
|
|
547
628
|
|
|
548
629
|
* [Development Workflow](CONTRIBUTING.md#development-workflow)
|
|
549
630
|
* [Sending a PR](CONTRIBUTING.md#sending-a-pull-request)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
export const BOAT_POSE_CONFIG = {
|
|
4
|
+
name: 'Boat Pose (Navasana)',
|
|
5
|
+
type: 'hold',
|
|
6
|
+
angles: [{
|
|
7
|
+
name: 'hipFlexion',
|
|
8
|
+
landmarkA: 11,
|
|
9
|
+
landmarkB: 23,
|
|
10
|
+
landmarkC: 25
|
|
11
|
+
}, {
|
|
12
|
+
name: 'kneeAngle',
|
|
13
|
+
landmarkA: 23,
|
|
14
|
+
landmarkB: 25,
|
|
15
|
+
landmarkC: 27
|
|
16
|
+
}],
|
|
17
|
+
phases: [{
|
|
18
|
+
phase: 'hold',
|
|
19
|
+
angleName: 'hipFlexion',
|
|
20
|
+
minAngle: 60,
|
|
21
|
+
maxAngle: 110
|
|
22
|
+
}],
|
|
23
|
+
repSequence: [],
|
|
24
|
+
formRules: [{
|
|
25
|
+
name: 'backRounding',
|
|
26
|
+
message: "Lengthen your spine — don't round your back",
|
|
27
|
+
severity: 'warning',
|
|
28
|
+
angleName: 'hipFlexion',
|
|
29
|
+
minAngle: 60,
|
|
30
|
+
maxAngle: 110
|
|
31
|
+
}, {
|
|
32
|
+
name: 'legsDropping',
|
|
33
|
+
message: 'Keep your legs lifted',
|
|
34
|
+
severity: 'warning',
|
|
35
|
+
angleName: 'kneeAngle',
|
|
36
|
+
minAngle: 150,
|
|
37
|
+
maxAngle: 180
|
|
38
|
+
}],
|
|
39
|
+
holdDurationMs: 30000
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=boat-pose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["BOAT_POSE_CONFIG","name","type","angles","landmarkA","landmarkB","landmarkC","phases","phase","angleName","minAngle","maxAngle","repSequence","formRules","message","severity","holdDurationMs"],"sourceRoot":"../../../src","sources":["config/boat-pose.ts"],"mappings":";;AAEA,OAAO,MAAMA,gBAAgC,GAAG;EAC9CC,IAAI,EAAE,sBAAsB;EAC5BC,IAAI,EAAE,MAAM;EACZC,MAAM,EAAE,CACN;IAAEF,IAAI,EAAE,YAAY;IAAEG,SAAS,EAAE,EAAE;IAAEC,SAAS,EAAE,EAAE;IAAEC,SAAS,EAAE;EAAG,CAAC,EACnE;IAAEL,IAAI,EAAE,WAAW;IAAEG,SAAS,EAAE,EAAE;IAAEC,SAAS,EAAE,EAAE;IAAEC,SAAS,EAAE;EAAG,CAAC,CACnE;EACDC,MAAM,EAAE,CACN;IAAEC,KAAK,EAAE,MAAM;IAAEC,SAAS,EAAE,YAAY;IAAEC,QAAQ,EAAE,EAAE;IAAEC,QAAQ,EAAE;EAAI,CAAC,CACxE;EACDC,WAAW,EAAE,EAAE;EACfC,SAAS,EAAE,CACT;IACEZ,IAAI,EAAE,cAAc;IACpBa,OAAO,EAAE,6CAA6C;IACtDC,QAAQ,EAAE,SAAS;IACnBN,SAAS,EAAE,YAAY;IACvBC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE;EACZ,CAAC,EACD;IACEV,IAAI,EAAE,cAAc;IACpBa,OAAO,EAAE,uBAAuB;IAChCC,QAAQ,EAAE,SAAS;IACnBN,SAAS,EAAE,WAAW;IACtBC,QAAQ,EAAE,GAAG;IACbC,QAAQ,EAAE;EACZ,CAAC,CACF;EACDK,cAAc,EAAE;AAClB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
export const BOW_POSE_CONFIG = {
|
|
4
|
+
name: 'Bow Pose (Dhanurasana)',
|
|
5
|
+
type: 'hold',
|
|
6
|
+
angles: [{
|
|
7
|
+
name: 'kneeAngle',
|
|
8
|
+
landmarkA: 23,
|
|
9
|
+
landmarkB: 25,
|
|
10
|
+
landmarkC: 27
|
|
11
|
+
}, {
|
|
12
|
+
name: 'shoulderExtension',
|
|
13
|
+
landmarkA: 23,
|
|
14
|
+
landmarkB: 11,
|
|
15
|
+
landmarkC: 15
|
|
16
|
+
}],
|
|
17
|
+
phases: [{
|
|
18
|
+
phase: 'hold',
|
|
19
|
+
angleName: 'kneeAngle',
|
|
20
|
+
minAngle: 50,
|
|
21
|
+
maxAngle: 100
|
|
22
|
+
}],
|
|
23
|
+
repSequence: [],
|
|
24
|
+
formRules: [{
|
|
25
|
+
name: 'kneesTooWide',
|
|
26
|
+
message: 'Keep your knees hip-width apart',
|
|
27
|
+
severity: 'warning',
|
|
28
|
+
angleName: 'kneeAngle',
|
|
29
|
+
minAngle: 50,
|
|
30
|
+
maxAngle: 100
|
|
31
|
+
}],
|
|
32
|
+
holdDurationMs: 30000
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=bow-pose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["BOW_POSE_CONFIG","name","type","angles","landmarkA","landmarkB","landmarkC","phases","phase","angleName","minAngle","maxAngle","repSequence","formRules","message","severity","holdDurationMs"],"sourceRoot":"../../../src","sources":["config/bow-pose.ts"],"mappings":";;AACA,OAAO,MAAMA,eAA+B,GAAG;EAC7CC,IAAI,EAAE,wBAAwB;EAC9BC,IAAI,EAAE,MAAM;EACZC,MAAM,EAAE,CACN;IAAEF,IAAI,EAAE,WAAW;IAAEG,SAAS,EAAE,EAAE;IAAEC,SAAS,EAAE,EAAE;IAAEC,SAAS,EAAE;EAAG,CAAC,EAClE;IAAEL,IAAI,EAAE,mBAAmB;IAAEG,SAAS,EAAE,EAAE;IAAEC,SAAS,EAAE,EAAE;IAAEC,SAAS,EAAE;EAAG,CAAC,CAC3E;EACDC,MAAM,EAAE,CACN;IAAEC,KAAK,EAAE,MAAM;IAAEC,SAAS,EAAE,WAAW;IAAEC,QAAQ,EAAE,EAAE;IAAEC,QAAQ,EAAE;EAAI,CAAC,CACvE;EACDC,WAAW,EAAE,EAAE;EACfC,SAAS,EAAE,CACT;IACEZ,IAAI,EAAE,cAAc;IACpBa,OAAO,EAAE,iCAAiC;IAC1CC,QAAQ,EAAE,SAAS;IACnBN,SAAS,EAAE,WAAW;IACtBC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE;EACZ,CAAC,CACF;EACDK,cAAc,EAAE;AAClB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
export const BRIDGE_POSE_CONFIG = {
|
|
4
|
+
name: 'Bridge Pose (Setu Bandhasana)',
|
|
5
|
+
type: 'hold',
|
|
6
|
+
angles: [{
|
|
7
|
+
name: 'hipExtension',
|
|
8
|
+
landmarkA: 11,
|
|
9
|
+
landmarkB: 23,
|
|
10
|
+
landmarkC: 25
|
|
11
|
+
}, {
|
|
12
|
+
name: 'kneeAngle',
|
|
13
|
+
landmarkA: 23,
|
|
14
|
+
landmarkB: 25,
|
|
15
|
+
landmarkC: 27
|
|
16
|
+
}],
|
|
17
|
+
phases: [{
|
|
18
|
+
phase: 'hold',
|
|
19
|
+
angleName: 'kneeAngle',
|
|
20
|
+
minAngle: 80,
|
|
21
|
+
maxAngle: 110
|
|
22
|
+
}],
|
|
23
|
+
repSequence: [],
|
|
24
|
+
formRules: [{
|
|
25
|
+
name: 'hipsLow',
|
|
26
|
+
message: 'Push your hips up higher',
|
|
27
|
+
severity: 'warning',
|
|
28
|
+
angleName: 'hipExtension',
|
|
29
|
+
minAngle: 140,
|
|
30
|
+
maxAngle: 180
|
|
31
|
+
}],
|
|
32
|
+
holdDurationMs: 30000
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=bridge-pose.js.map
|