react-native-nitro-pose-exercises 1.1.5 → 1.1.7

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.
Files changed (225) hide show
  1. package/README.md +192 -159
  2. package/android/src/main/java/com/margelo/nitro/nitroposeexercises/NitroPoseExercises.kt +121 -4
  3. package/ios/NitroPoseExercises.swift +128 -11
  4. package/lib/module/NitroPoseExercises.nitro.js.map +1 -1
  5. package/lib/module/config/bicep-curl.js +3 -0
  6. package/lib/module/config/bicep-curl.js.map +1 -1
  7. package/lib/module/config/boat-pose.js +44 -0
  8. package/lib/module/config/boat-pose.js.map +1 -0
  9. package/lib/module/config/bow-pose.js +37 -0
  10. package/lib/module/config/bow-pose.js.map +1 -0
  11. package/lib/module/config/bridge-pose.js +37 -0
  12. package/lib/module/config/bridge-pose.js.map +1 -0
  13. package/lib/module/config/calf-raise.js +47 -0
  14. package/lib/module/config/calf-raise.js.map +1 -0
  15. package/lib/module/config/camel-pose.js +37 -0
  16. package/lib/module/config/camel-pose.js.map +1 -0
  17. package/lib/module/config/chair-pose.js +3 -0
  18. package/lib/module/config/chair-pose.js.map +1 -1
  19. package/lib/module/config/childs-pose.js +42 -0
  20. package/lib/module/config/childs-pose.js.map +1 -0
  21. package/lib/module/config/cobra-pose.js +3 -0
  22. package/lib/module/config/cobra-pose.js.map +1 -1
  23. package/lib/module/config/cobra-wings.js +47 -0
  24. package/lib/module/config/cobra-wings.js.map +1 -0
  25. package/lib/module/config/dead-lift.js +54 -0
  26. package/lib/module/config/dead-lift.js.map +1 -0
  27. package/lib/module/config/downward-dog.js +3 -0
  28. package/lib/module/config/downward-dog.js.map +1 -1
  29. package/lib/module/config/extended-side-angle.js +49 -0
  30. package/lib/module/config/extended-side-angle.js.map +1 -0
  31. package/lib/module/config/fish-pose.js +37 -0
  32. package/lib/module/config/fish-pose.js.map +1 -0
  33. package/lib/module/config/front-raise.js +54 -0
  34. package/lib/module/config/front-raise.js.map +1 -0
  35. package/lib/module/config/glute-bridge.js +49 -0
  36. package/lib/module/config/glute-bridge.js.map +1 -0
  37. package/lib/module/config/hip-abduction.js +42 -0
  38. package/lib/module/config/hip-abduction.js.map +1 -0
  39. package/lib/module/config/knee-raise.js +47 -0
  40. package/lib/module/config/knee-raise.js.map +1 -0
  41. package/lib/module/config/lateral-raise.js +54 -0
  42. package/lib/module/config/lateral-raise.js.map +1 -0
  43. package/lib/module/config/leg-raise.js +49 -0
  44. package/lib/module/config/leg-raise.js.map +1 -0
  45. package/lib/module/config/lunge.js +3 -0
  46. package/lib/module/config/lunge.js.map +1 -1
  47. package/lib/module/config/mountain-pose.js +49 -0
  48. package/lib/module/config/mountain-pose.js.map +1 -0
  49. package/lib/module/config/overarm-reach.js +47 -0
  50. package/lib/module/config/overarm-reach.js.map +1 -0
  51. package/lib/module/config/plank.js +3 -0
  52. package/lib/module/config/plank.js.map +1 -1
  53. package/lib/module/config/pull-up.js +47 -0
  54. package/lib/module/config/pull-up.js.map +1 -0
  55. package/lib/module/config/pushup.js +4 -0
  56. package/lib/module/config/pushup.js.map +1 -1
  57. package/lib/module/config/reverse-warrior.js +49 -0
  58. package/lib/module/config/reverse-warrior.js.map +1 -0
  59. package/lib/module/config/shoulder-press.js +3 -0
  60. package/lib/module/config/shoulder-press.js.map +1 -1
  61. package/lib/module/config/side-lung.js +54 -0
  62. package/lib/module/config/side-lung.js.map +1 -0
  63. package/lib/module/config/side-plank.js +37 -0
  64. package/lib/module/config/side-plank.js.map +1 -0
  65. package/lib/module/config/situp.js +3 -0
  66. package/lib/module/config/situp.js.map +1 -1
  67. package/lib/module/config/squat.js +3 -0
  68. package/lib/module/config/squat.js.map +1 -1
  69. package/lib/module/config/sumo-squat.js +54 -0
  70. package/lib/module/config/sumo-squat.js.map +1 -0
  71. package/lib/module/config/tree-pose.js +3 -0
  72. package/lib/module/config/tree-pose.js.map +1 -1
  73. package/lib/module/config/triangle-pose.js +49 -0
  74. package/lib/module/config/triangle-pose.js.map +1 -0
  75. package/lib/module/config/tricep-dip.js +4 -0
  76. package/lib/module/config/tricep-dip.js.map +1 -1
  77. package/lib/module/config/v-up.js +42 -0
  78. package/lib/module/config/v-up.js.map +1 -0
  79. package/lib/module/config/wall-sit.js +3 -0
  80. package/lib/module/config/wall-sit.js.map +1 -1
  81. package/lib/module/config/warrior-i.js +3 -0
  82. package/lib/module/config/warrior-i.js.map +1 -1
  83. package/lib/module/config/warrior-ii.js +3 -0
  84. package/lib/module/config/warrior-ii.js.map +1 -1
  85. package/lib/module/config/warrior-iii.js +61 -0
  86. package/lib/module/config/warrior-iii.js.map +1 -0
  87. package/lib/module/index.js +31 -3
  88. package/lib/module/index.js.map +1 -1
  89. package/lib/typescript/src/NitroPoseExercises.nitro.d.ts +9 -1
  90. package/lib/typescript/src/NitroPoseExercises.nitro.d.ts.map +1 -1
  91. package/lib/typescript/src/config/bicep-curl.d.ts.map +1 -1
  92. package/lib/typescript/src/config/boat-pose.d.ts +3 -0
  93. package/lib/typescript/src/config/boat-pose.d.ts.map +1 -0
  94. package/lib/typescript/src/config/bow-pose.d.ts +3 -0
  95. package/lib/typescript/src/config/bow-pose.d.ts.map +1 -0
  96. package/lib/typescript/src/config/bridge-pose.d.ts +3 -0
  97. package/lib/typescript/src/config/bridge-pose.d.ts.map +1 -0
  98. package/lib/typescript/src/config/calf-raise.d.ts +3 -0
  99. package/lib/typescript/src/config/calf-raise.d.ts.map +1 -0
  100. package/lib/typescript/src/config/camel-pose.d.ts +3 -0
  101. package/lib/typescript/src/config/camel-pose.d.ts.map +1 -0
  102. package/lib/typescript/src/config/chair-pose.d.ts.map +1 -1
  103. package/lib/typescript/src/config/childs-pose.d.ts +3 -0
  104. package/lib/typescript/src/config/childs-pose.d.ts.map +1 -0
  105. package/lib/typescript/src/config/cobra-pose.d.ts.map +1 -1
  106. package/lib/typescript/src/config/cobra-wings.d.ts +3 -0
  107. package/lib/typescript/src/config/cobra-wings.d.ts.map +1 -0
  108. package/lib/typescript/src/config/dead-lift.d.ts +3 -0
  109. package/lib/typescript/src/config/dead-lift.d.ts.map +1 -0
  110. package/lib/typescript/src/config/downward-dog.d.ts.map +1 -1
  111. package/lib/typescript/src/config/extended-side-angle.d.ts +3 -0
  112. package/lib/typescript/src/config/extended-side-angle.d.ts.map +1 -0
  113. package/lib/typescript/src/config/fish-pose.d.ts +3 -0
  114. package/lib/typescript/src/config/fish-pose.d.ts.map +1 -0
  115. package/lib/typescript/src/config/front-raise.d.ts +3 -0
  116. package/lib/typescript/src/config/front-raise.d.ts.map +1 -0
  117. package/lib/typescript/src/config/glute-bridge.d.ts +3 -0
  118. package/lib/typescript/src/config/glute-bridge.d.ts.map +1 -0
  119. package/lib/typescript/src/config/hip-abduction.d.ts +3 -0
  120. package/lib/typescript/src/config/hip-abduction.d.ts.map +1 -0
  121. package/lib/typescript/src/config/knee-raise.d.ts +3 -0
  122. package/lib/typescript/src/config/knee-raise.d.ts.map +1 -0
  123. package/lib/typescript/src/config/lateral-raise.d.ts +3 -0
  124. package/lib/typescript/src/config/lateral-raise.d.ts.map +1 -0
  125. package/lib/typescript/src/config/leg-raise.d.ts +3 -0
  126. package/lib/typescript/src/config/leg-raise.d.ts.map +1 -0
  127. package/lib/typescript/src/config/lunge.d.ts.map +1 -1
  128. package/lib/typescript/src/config/mountain-pose.d.ts +3 -0
  129. package/lib/typescript/src/config/mountain-pose.d.ts.map +1 -0
  130. package/lib/typescript/src/config/overarm-reach.d.ts +3 -0
  131. package/lib/typescript/src/config/overarm-reach.d.ts.map +1 -0
  132. package/lib/typescript/src/config/plank.d.ts.map +1 -1
  133. package/lib/typescript/src/config/pull-up.d.ts +3 -0
  134. package/lib/typescript/src/config/pull-up.d.ts.map +1 -0
  135. package/lib/typescript/src/config/pushup.d.ts.map +1 -1
  136. package/lib/typescript/src/config/reverse-warrior.d.ts +3 -0
  137. package/lib/typescript/src/config/reverse-warrior.d.ts.map +1 -0
  138. package/lib/typescript/src/config/shoulder-press.d.ts.map +1 -1
  139. package/lib/typescript/src/config/side-lung.d.ts +3 -0
  140. package/lib/typescript/src/config/side-lung.d.ts.map +1 -0
  141. package/lib/typescript/src/config/side-plank.d.ts +3 -0
  142. package/lib/typescript/src/config/side-plank.d.ts.map +1 -0
  143. package/lib/typescript/src/config/situp.d.ts.map +1 -1
  144. package/lib/typescript/src/config/squat.d.ts.map +1 -1
  145. package/lib/typescript/src/config/sumo-squat.d.ts +3 -0
  146. package/lib/typescript/src/config/sumo-squat.d.ts.map +1 -0
  147. package/lib/typescript/src/config/tree-pose.d.ts.map +1 -1
  148. package/lib/typescript/src/config/triangle-pose.d.ts +3 -0
  149. package/lib/typescript/src/config/triangle-pose.d.ts.map +1 -0
  150. package/lib/typescript/src/config/tricep-dip.d.ts.map +1 -1
  151. package/lib/typescript/src/config/v-up.d.ts +3 -0
  152. package/lib/typescript/src/config/v-up.d.ts.map +1 -0
  153. package/lib/typescript/src/config/wall-sit.d.ts.map +1 -1
  154. package/lib/typescript/src/config/warrior-i.d.ts.map +1 -1
  155. package/lib/typescript/src/config/warrior-ii.d.ts.map +1 -1
  156. package/lib/typescript/src/config/warrior-iii.d.ts +3 -0
  157. package/lib/typescript/src/config/warrior-iii.d.ts.map +1 -0
  158. package/lib/typescript/src/index.d.ts +26 -0
  159. package/lib/typescript/src/index.d.ts.map +1 -1
  160. package/nitrogen/generated/android/c++/JCameraAngleType.hpp +58 -0
  161. package/nitrogen/generated/android/c++/JExerciseConfig.hpp +19 -3
  162. package/nitrogen/generated/android/c++/JHybridNitroPoseExercisesSpec.cpp +47 -0
  163. package/nitrogen/generated/android/c++/JHybridNitroPoseExercisesSpec.hpp +5 -0
  164. package/nitrogen/generated/android/c++/JPostureFamily.hpp +73 -0
  165. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroposeexercises/CameraAngleType.kt +23 -0
  166. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroposeexercises/ExerciseConfig.kt +19 -4
  167. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroposeexercises/HybridNitroPoseExercisesSpec.kt +32 -0
  168. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroposeexercises/PostureFamily.kt +28 -0
  169. package/nitrogen/generated/ios/NitroPoseExercises-Swift-Cxx-Bridge.hpp +9 -0
  170. package/nitrogen/generated/ios/NitroPoseExercises-Swift-Cxx-Umbrella.hpp +6 -0
  171. package/nitrogen/generated/ios/c++/HybridNitroPoseExercisesSpecSwift.hpp +28 -0
  172. package/nitrogen/generated/ios/swift/CameraAngleType.swift +40 -0
  173. package/nitrogen/generated/ios/swift/ExerciseConfig.swift +17 -2
  174. package/nitrogen/generated/ios/swift/HybridNitroPoseExercisesSpec.swift +3 -0
  175. package/nitrogen/generated/ios/swift/HybridNitroPoseExercisesSpec_cxx.swift +76 -0
  176. package/nitrogen/generated/ios/swift/PostureFamily.swift +60 -0
  177. package/nitrogen/generated/shared/c++/CameraAngleType.hpp +76 -0
  178. package/nitrogen/generated/shared/c++/ExerciseConfig.hpp +20 -2
  179. package/nitrogen/generated/shared/c++/HybridNitroPoseExercisesSpec.cpp +5 -0
  180. package/nitrogen/generated/shared/c++/HybridNitroPoseExercisesSpec.hpp +5 -0
  181. package/nitrogen/generated/shared/c++/PostureFamily.hpp +96 -0
  182. package/package.json +1 -1
  183. package/src/NitroPoseExercises.nitro.ts +19 -0
  184. package/src/config/bicep-curl.ts +3 -0
  185. package/src/config/boat-pose.ts +36 -0
  186. package/src/config/bow-pose.ts +27 -0
  187. package/src/config/bridge-pose.ts +28 -0
  188. package/src/config/calf-raise.ts +30 -0
  189. package/src/config/camel-pose.ts +27 -0
  190. package/src/config/chair-pose.ts +3 -0
  191. package/src/config/childs-pose.ts +26 -0
  192. package/src/config/cobra-pose.ts +3 -0
  193. package/src/config/cobra-wings.ts +29 -0
  194. package/src/config/dead-lift.ts +38 -0
  195. package/src/config/downward-dog.ts +3 -0
  196. package/src/config/extended-side-angle.ts +36 -0
  197. package/src/config/fish-pose.ts +28 -0
  198. package/src/config/front-raise.ts +58 -0
  199. package/src/config/glute-bridge.ts +36 -0
  200. package/src/config/hip-abduction.ts +28 -0
  201. package/src/config/knee-raise.ts +29 -0
  202. package/src/config/lateral-raise.ts +58 -0
  203. package/src/config/leg-raise.ts +36 -0
  204. package/src/config/lunge.ts +3 -0
  205. package/src/config/mountain-pose.ts +36 -0
  206. package/src/config/overarm-reach.ts +49 -0
  207. package/src/config/plank.ts +3 -0
  208. package/src/config/pull-up.ts +30 -0
  209. package/src/config/pushup.ts +3 -0
  210. package/src/config/reverse-warrior.ts +36 -0
  211. package/src/config/shoulder-press.ts +3 -0
  212. package/src/config/side-lung.ts +37 -0
  213. package/src/config/side-plank.ts +28 -0
  214. package/src/config/situp.ts +3 -0
  215. package/src/config/squat.ts +3 -0
  216. package/src/config/sumo-squat.ts +38 -0
  217. package/src/config/tree-pose.ts +3 -0
  218. package/src/config/triangle-pose.ts +37 -0
  219. package/src/config/tricep-dip.ts +3 -0
  220. package/src/config/v-up.ts +29 -0
  221. package/src/config/wall-sit.ts +3 -0
  222. package/src/config/warrior-i.ts +3 -0
  223. package/src/config/warrior-ii.ts +3 -0
  224. package/src/config/warrior-iii.ts +45 -0
  225. package/src/index.tsx +31 -3
package/README.md CHANGED
@@ -8,12 +8,15 @@
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
- * 🏋️ **Rep Counting** — Automatic rep detection with configurable state machines
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 configurable angle-based rules
14
- * 💀 **Skeleton Overlay** — Optional Skia-powered skeleton rendering over the camera feed
14
+ * 📐 **Form Validation** — Real-time form feedback with angle-based rules
15
+ * 🚦 **Posture Gating** — Refuses to count reps unless the user is in valid posture; "Get in position" feedback before sessions start
16
+ * 💀 **Skeleton Overlay** — Skia-powered skeleton with glow effects and live angle badges
15
17
  * ⚡ **Fully Native** — OS-level pose detection via Nitro Modules, zero JS bridge overhead
16
18
  * 📦 **Zero Model Bundling** — No ML model files to download or ship with your app
19
+ * 🪶 **~200 KB** — Virtually zero app size impact
17
20
 
18
21
  ---
19
22
 
@@ -56,15 +59,15 @@ cd ios && pod install
56
59
 
57
60
  <table>
58
61
  <tr>
59
- <th align="center">🍏 iOS Normal Mode</th>
60
- <th align="center">🍏 iOS Skia Mode</th>
62
+ <th align="center">📸 Normal Mode</th>
63
+ <th align="center">💀 Skeleton + Angle Overlay</th>
61
64
  </tr>
62
65
  <tr>
63
66
  <td align="center">
64
- <img alt="android" src="./docs/img/normal-iOS.png" height="650" width="300"/>
67
+ <img alt="normal-mode" src="./docs/img/normal.png" height="650" width="300"/>
65
68
  </td>
66
69
  <td align="center">
67
- <img alt="android" src="./docs/img/skia-iOS.png" height="650" width="300"/>
70
+ <img alt="skeleton-mode" src="./docs/img/skeleton.png" height="650" width="300"/>
68
71
  </td>
69
72
  </tr>
70
73
  </table>
@@ -75,12 +78,13 @@ cd ios && pod install
75
78
 
76
79
  | Feature | Description |
77
80
  | --- | --- |
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
- | **Flow-Based Exercises** | Ordered sequence of poses. Sun salutation, yoga flows. *(coming soon)* |
81
- | **Form Feedback** | Angle-based rules with throttled real-time callbacks. |
82
- | **Skeleton Overlay** | Body skeleton drawn over camera via Skia (19 joints iOS, 33 joints Android). |
81
+ | **Rep-Based Exercises** | Cyclic state machine (UP → DOWN → UP = 1 rep). Push-ups, squats, curls, and more. |
82
+ | **Hold-Based Exercises** | Single target pose with duration + stability tracking. Planks, wall sits, yoga poses. |
83
+ | **Posture Gate** | Family-based posture validation. Refuses to start or count reps until user is in correct position (e.g. horizontal for pushups, upright for squats). |
84
+ | **Form Feedback** | Angle-based rules with throttled real-time callbacks. Bad form blocks rep counting. |
85
+ | **Skeleton Overlay** | Glow-effect bones, color-coded joints, and live angle badges drawn over camera via Skia. |
83
86
  | **Bilateral Tracking** | Left and right side angles tracked independently. |
87
+ | **Fatigue Guard** | Minimum 800ms per rep prevents false counts. Form score gate rejects bad reps. |
84
88
 
85
89
  ---
86
90
 
@@ -90,8 +94,8 @@ cd ios && pod install
90
94
 
91
95
  Unlike MediaPipe-based solutions, this library uses OS-native APIs. There is **no model file to download or bundle**.
92
96
 
93
- * **iOS:** Apple Vision is a system framework — it's already on every iPhone running iOS 14+.
94
- * **Android:** ML Kit manages its own model via Google Play Services — it downloads and updates automatically.
97
+ * **iOS:** Apple Vision is a system framework — already on every iPhone running iOS 14+.
98
+ * **Android:** ML Kit manages its own model via Google Play Services — downloads and updates automatically.
95
99
 
96
100
  ### Permissions
97
101
 
@@ -124,18 +128,14 @@ module.exports = {
124
128
 
125
129
  ### Podspec (for library authors)
126
130
 
127
- The iOS podspec needs the Vision and AVFoundation system frameworks:
128
-
129
131
  ```ruby
130
- s.frameworks = ["AVFoundation", "Vision"]
132
+ s.frameworks = ['Vision', 'AVFoundation']
131
133
  ```
132
134
 
133
135
  No CocoaPods dependencies required — Vision is built into iOS.
134
136
 
135
137
  ### Android Gradle (for library authors)
136
138
 
137
- Add ML Kit Pose Detection to `android/build.gradle`:
138
-
139
139
  ```groovy
140
140
  dependencies {
141
141
  implementation 'com.google.mlkit:pose-detection:18.0.0-beta5'
@@ -149,8 +149,8 @@ dependencies {
149
149
  ### Basic — Normal Camera (No Skeleton)
150
150
 
151
151
  ```tsx
152
- import { useEffect, useCallback, useState } from 'react';
153
- import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
152
+ import { useEffect, useState } from 'react';
153
+ import { StyleSheet, View, Text } from 'react-native';
154
154
  import {
155
155
  Camera,
156
156
  useCameraDevice,
@@ -162,7 +162,6 @@ import {
162
162
  nitroPoseExercises,
163
163
  PUSHUP_CONFIG,
164
164
  type RepData,
165
- type FormFeedback,
166
165
  type SessionResult,
167
166
  } from 'react-native-nitro-pose-exercises';
168
167
 
@@ -176,7 +175,6 @@ export default function App() {
176
175
  if (!hasPermission) requestPermission();
177
176
  }, [hasPermission]);
178
177
 
179
- // Initialize pose engine — modelPath is ignored (OS-native, no model file)
180
178
  useEffect(() => {
181
179
  async function init() {
182
180
  await nitroPoseExercises.initialize('');
@@ -184,30 +182,19 @@ export default function App() {
184
182
 
185
183
  nitroPoseExercises.onRepComplete = (data: RepData) => {
186
184
  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
185
  };
193
186
 
194
187
  nitroPoseExercises.onSessionComplete = (result: SessionResult) => {
195
- console.log(
196
- `Done! ${result.totalReps} reps, avg form: ${result.averageFormScore}`
197
- );
188
+ console.log(`Done! ${result.totalReps} reps, form: ${result.averageFormScore}`);
198
189
  };
199
190
 
200
- // Start: 10 target reps, 3 second countdown
201
191
  nitroPoseExercises.startSession(10, 3);
202
192
  }
203
193
 
204
194
  init();
205
- return () => {
206
- nitroPoseExercises.release();
207
- };
195
+ return () => { nitroPoseExercises.release(); };
208
196
  }, []);
209
197
 
210
- // Frame processor
211
198
  const frameOutput = useFrameOutput({
212
199
  pixelFormat: 'rgb',
213
200
  onFrame(frame) {
@@ -251,75 +238,6 @@ const styles = StyleSheet.create({
251
238
  });
252
239
  ```
253
240
 
254
- ### Skeleton Overlay — SkiaCamera
255
-
256
- ```tsx
257
- import { SkiaCamera } from 'react-native-vision-camera-skia'
258
- import { Skia } from '@shopify/react-native-skia'
259
- import { nitroPoseExercises } from 'react-native-nitro-pose-exercises'
260
-
261
- const SKELETON_CONNECTIONS: [number, number][] = [
262
- [11, 12], [11, 23], [12, 24], [23, 24], // Torso
263
- [11, 13], [13, 15], // Left arm
264
- [12, 14], [14, 16], // Right arm
265
- [23, 25], [25, 27], // Left leg
266
- [24, 26], [26, 28], // Right leg
267
- ]
268
-
269
- <SkiaCamera
270
- style={StyleSheet.absoluteFill}
271
- isActive={true}
272
- device="back"
273
- pixelFormat="rgb"
274
- onFrame={(frame, render) => {
275
- 'worklet'
276
- try {
277
- nitroPoseExercises.processFrame(frame)
278
- const landmarks = nitroPoseExercises.landmarks
279
-
280
- render(({ frameTexture, canvas }) => {
281
- canvas.drawImage(frameTexture, 0, 0)
282
-
283
- 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)
292
-
293
- for (const [i, j] of SKELETON_CONNECTIONS) {
294
- if (i < landmarks.length && j < landmarks.length) {
295
- const a = landmarks[i]
296
- const b = landmarks[j]
297
- if (a.visibility > 0.5 && b.visibility > 0.5) {
298
- canvas.drawLine(a.x * w, a.y * h, b.x * w, b.y * h, linePaint)
299
- }
300
- }
301
- }
302
-
303
- // Draw joints
304
- const jointPaint = Skia.Paint()
305
- jointPaint.setColor(Skia.Color('#00FFFF'))
306
- jointPaint.setStyle(0)
307
-
308
- for (let idx = 0; idx < landmarks.length; idx++) {
309
- const lm = landmarks[idx]
310
- if (lm.visibility > 0.5) {
311
- canvas.drawCircle(lm.x * w, lm.y * h, 6, jointPaint)
312
- }
313
- }
314
- }
315
- })
316
- } finally {
317
- frame.dispose()
318
- }
319
- }}
320
- />
321
- ```
322
-
323
241
  ---
324
242
 
325
243
  ## 🧩 API Reference
@@ -337,7 +255,6 @@ release(): void
337
255
  ### Exercise Setup
338
256
 
339
257
  ```ts
340
- // Load an exercise config (built-in or custom)
341
258
  loadExercise(config: ExerciseConfig): void
342
259
  ```
343
260
 
@@ -348,12 +265,15 @@ startSession(targetReps: number, countdownSeconds: number): void
348
265
  pauseSession(): void
349
266
  resumeSession(): void
350
267
  stopSession(): void
268
+ // Returns true if the user is currently in valid posture for the loaded exercise.
269
+ // Poll this before starting a session, e.g. show "Get in position" until ready.
270
+ isReady(): boolean
351
271
  ```
352
272
 
353
273
  ### Frame Processing
354
274
 
355
275
  ```ts
356
- // Pass VisionCamera frame for pose detection — call from frame processor
276
+ // Call from VisionCamera frame processor worklet
357
277
  processFrame(frame: Frame): void
358
278
  ```
359
279
 
@@ -363,7 +283,7 @@ processFrame(frame: Frame): void
363
283
  readonly status: SessionStatus // 'idle' | 'countdown' | 'active' | 'paused' | 'completed'
364
284
  readonly currentPhase: ExercisePhase // 'up' | 'down' | 'hold' | 'transition' | 'unknown'
365
285
  readonly repCount: number
366
- readonly landmarks: Landmark[] // Body landmarks (mapped to MediaPipe indices)
286
+ readonly landmarks: Landmark[] // Body landmarks mapped to MediaPipe indices
367
287
  ```
368
288
 
369
289
  ### Callbacks
@@ -375,21 +295,21 @@ onFormFeedback: ((feedback: FormFeedback) => void) | undefined
375
295
  onHoldProgress: ((progress: HoldProgress) => void) | undefined
376
296
  onPoseLost: (() => void) | undefined
377
297
  onPoseRegained: (() => void) | undefined
298
+ onPostureLost: (() => void) | undefined
299
+ onPostureRegained: (() => void) | undefined
378
300
  onSessionComplete: ((result: SessionResult) => void) | undefined
379
301
  ```
380
302
 
381
- ---
382
-
383
303
  ### Callback Payloads
384
304
 
385
305
  #### RepData
386
306
 
387
307
  ```ts
388
308
  {
389
- repNumber: number // Current rep count
390
- durationMs: number // Time taken for this rep
391
- formScore: number // 0-100 form quality score
392
- angles: AngleSnapshot[] // Joint angles at rep completion
309
+ repNumber: number
310
+ durationMs: number
311
+ formScore: number // 0-100
312
+ angles: AngleSnapshot[] // all tracked angles at rep completion
393
313
  }
394
314
  ```
395
315
 
@@ -397,9 +317,9 @@ onSessionComplete: ((result: SessionResult) => void) | undefined
397
317
 
398
318
  ```ts
399
319
  {
400
- ruleName: string // e.g. 'hipSag'
401
- message: string // e.g. 'Keep your hips up'
402
- severity: FormSeverity // 'info' | 'warning' | 'error'
320
+ ruleName: string
321
+ message: string
322
+ severity: FormSeverity // 'info' | 'warning' | 'error'
403
323
  }
404
324
  ```
405
325
 
@@ -415,40 +335,139 @@ onSessionComplete: ((result: SessionResult) => void) | undefined
415
335
  angleHistory: AngleSnapshot[]
416
336
  }
417
337
  ```
338
+ ---
339
+
340
+ ## 🚦 Posture Gating
341
+
342
+ Each exercise config declares a **posture family** that defines what body position is required before reps are counted. This prevents false counts — e.g. waving your arm while standing won't count as a push-up.
343
+
344
+ ### Posture Families
345
+
346
+ | Family | Description | Used For |
347
+ | --- | --- | --- |
348
+ | `horizontalProne` | Body horizontal, face down. Shoulders, hips, ankles in a horizontal band. | Push-ups, planks, cobra, mountain climbers |
349
+ | `standingUpright` | Standing, shoulders above hips above knees. | Squats, lunges, curls, presses, most yoga poses |
350
+ | `seated` | Hips near knees, shoulders above hips. | Boat pose, seated yoga, child's pose |
351
+ | `supine` | Body horizontal, face up. | Sit-ups, glute bridge, leg raises |
352
+ | `sidePlank` | Body horizontal, rotated to one side. | Side plank, side leg raises |
353
+ | `inverted` | Hips higher than shoulders and ankles. | Downward dog, handstand |
354
+ | `none` | No posture gating. | Custom or unconstrained exercises |
355
+
356
+ ### Flow
357
+
358
+ loadExercise(config) → poll isReady() → user gets in position →
359
+ isReady() returns true → startSession() → reps counted normally
360
+
361
+ if posture breaks mid-session
362
+ → onPostureLost fires
363
+ → phase detection pauses
364
+ → in-progress rep discarded
365
+
366
+ user re-enters position
367
+ → onPostureRegained fires
368
+ → counting resumes
369
+
370
+ ### Example: Wait for Position Before Starting
371
+
372
+ ```tsx
373
+ const [isInPosition, setIsInPosition] = useState(false);
374
+
375
+ useEffect(() => {
376
+ // Wait for the user to get into position before starting
377
+ const checkInterval = setInterval(() => {
378
+ if (nitroPoseExercises.isReady()) {
379
+ clearInterval(checkInterval);
380
+ nitroPoseExercises.startSession(10, 3);
381
+ }
382
+ }, 300);
383
+ return () => clearInterval(interval);
384
+ }, []);
385
+
386
+ useEffect(() => {
387
+ nitroPoseExercises.onPostureLost = () => {
388
+ setMessage('Get back into position');
389
+ };
390
+ nitroPoseExercises.onPostureRegained = () => {
391
+ setMessage('');
392
+ };
393
+ }, []);
394
+
395
+ return (
396
+ <>
397
+ {!isInPosition && <Text>Get into push-up position</Text>}
398
+ {isInPosition && <Text>Hold still — starting...</Text>}
399
+ </>
400
+ );
401
+ ```
402
+
403
+ ### Tuning
404
+
405
+ Posture gates use a **10-frame hysteresis** (about 1 second at 30fps with frame throttling) — single-frame failures don't pause the session. This prevents flicker from momentary occlusion or visibility drops.
418
406
 
419
407
  ---
420
408
 
421
- ## 🏋️ Built-In Exercise Configs
409
+ ## 🏋️ All 38 Built-In Exercise Configs
422
410
 
423
- ### Rep-Based
411
+ ### Rep-Based: Strength (15 exercises)
424
412
 
425
- | Config | Exercise | Primary Angle | Form Rules |
413
+ | Config | Exercise | Primary Angle | Camera View |
414
+ | --- | --- | --- | --- |
415
+ | `PUSHUP_CONFIG` | Push-Up | Elbow 140°–180° / 30°–110° | Side |
416
+ | `PULL_UP_CONFIG` | Pull-Up | Elbow 150°–180° / 40°–90° | Side |
417
+ | `SQUAT_CONFIG` | Squat | Knee 155°–180° / 50°–105° | Side |
418
+ | `SUMO_SQUAT_CONFIG` | Sumo Squat | Knee 155°–180° / 60°–110° | Front |
419
+ | `BICEP_CURL_CONFIG` | Bicep Curl | Elbow 150°–180° / 25°–70° | Side |
420
+ | `SHOULDER_PRESS_CONFIG` | Shoulder Press | Elbow 155°–180° / 60°–100° | Side |
421
+ | `LUNGE_CONFIG` | Lunge | Front knee 155°–180° / 70°–110° | Side |
422
+ | `SIDE_LUNGE_CONFIG` | Side Lunge | Bent knee 155°–180° / 70°–110° | Front |
423
+ | `TRICEP_DIP_CONFIG` | Tricep Dip | Elbow 150°–180° / 60°–100° | Side |
424
+ | `DEADLIFT_CONFIG` | Deadlift | Hip 160°–180° / 60°–120° | Side |
425
+ | `LATERAL_RAISE_CONFIG` | Lateral Raise | Shoulder abduction 5°–30° / 75°–110° | Front |
426
+ | `FRONT_RAISE_CONFIG` | Front Raise | Shoulder flexion 0°–25° / 75°–110° | Side |
427
+ | `CALF_RAISE_CONFIG` | Calf Raise | Ankle 70°–95° / 110°–150° | Side |
428
+ | `OVERARM_REACH_CONFIG` | Overarm Reach | Shoulder abduction 0°–30° / 155°–180° | Front |
429
+ | `HIP_ABDUCTION_CONFIG` | Hip Abduction | Leg spread 0°–15° / 30°–60° | Front |
430
+
431
+ ### Rep-Based: Core (6 exercises)
432
+
433
+ | Config | Exercise | Primary Angle | Camera View |
426
434
  | --- | --- | --- | --- |
427
- | `PUSHUP_CONFIG` | Push-Up | Elbow (140°–180° up, 30°–110° down) | Hip sag, hip pike |
428
- | `SQUAT_CONFIG` | Squat | Knee (155°–180° up, 50°–105° down) | Knees caving, leaning forward |
429
- | `BICEP_CURL_CONFIG` | Bicep Curl | Elbow (150°–180° down, 25°–70° up) | Elbow flare, swinging |
430
- | `SHOULDER_PRESS_CONFIG` | Shoulder Press | Elbow (155°–180° up, 60°–100° down) | Back arch |
431
- | `LUNGE_CONFIG` | Lunge | Front knee (155°–180° up, 70°–110° down) | Knee over toe, torso lean |
432
- | `SITUP_CONFIG` | Sit-Up | Hip (130°–180° down, 40°–90° up) | Neck strain |
433
- | `TRICEP_DIP_CONFIG` | Tricep Dip | Elbow (150°–180° up, 60°–100° down) | Going too deep |
434
-
435
- ### Hold-Based
436
-
437
- | Config | Exercise | Hold Angle | Duration | Form Rules |
438
- | --- | --- | --- | --- | --- |
439
- | `PLANK_CONFIG` | Plank | Hip 155°–180° | 60s | Hip sag, hip pike |
440
- | `WALL_SIT_CONFIG` | Wall Sit | Knee 80°–110° | 45s | Too high, leaning forward |
441
-
442
- ### Yoga Poses
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 |
435
+ | `SITUP_CONFIG` | Sit-Up | Hip 130°–180° / 40°–90° | Side |
436
+ | `LEG_RAISE_CONFIG` | Leg Raise | Hip 150°–180° / 60°–110° | Side |
437
+ | `V_UP_CONFIG` | V-Up | Hip fold 150°–180° / 30°–80° | Side |
438
+ | `GLUTE_BRIDGE_CONFIG` | Glute Bridge | Hip extension 80°–120° / 155°–180° | Side |
439
+ | `COBRA_WINGS_CONFIG` | Cobra Wings | Hip extension 160°–180° / 120°–155° | Side |
440
+ | `KNEE_RAISE_CONFIG` | Knee Raise | Hip 155°–180° / 60°–110° | Side |
441
+
442
+ ### Hold-Based: Strength (3 exercises)
443
+
444
+ | Config | Exercise | Hold Angle | Default Duration |
445
+ | --- | --- | --- | --- |
446
+ | `PLANK_CONFIG` | Plank | Hip 155°–180° | 60s |
447
+ | `SIDE_PLANK_CONFIG` | Side Plank | Hip lateral 155°–180° | 30s |
448
+ | `WALL_SIT_CONFIG` | Wall Sit | Knee 80°–110° | 45s |
449
+
450
+ ### Hold-Based: Yoga (14 exercises)
451
+
452
+ | Config | Exercise | Hold Angle | Default Duration |
453
+ | --- | --- | --- | --- |
454
+ | `MOUNTAIN_POSE_CONFIG` | Mountain Pose (Tadasana) | Knee 170°–180° | 30s |
455
+ | `TREE_POSE_CONFIG` | Tree Pose (Vrksasana) | Standing leg 165°–180° | 30s |
456
+ | `CHAIR_POSE_CONFIG` | Chair Pose (Utkatasana) | Knee 90°–130° | 30s |
457
+ | `WARRIOR_I_CONFIG` | Warrior I (Virabhadrasana I) | Front knee 80°–110° | 30s |
458
+ | `WARRIOR_II_CONFIG` | Warrior II (Virabhadrasana II) | Front knee 80°–110° | 30s |
459
+ | `WARRIOR_III_CONFIG` | Warrior III (Virabhadrasana III) | Hip hinge 70°–110° | 30s |
460
+ | `REVERSE_WARRIOR_CONFIG` | Reverse Warrior | Front knee 80°–110° | 30s |
461
+ | `DOWNWARD_DOG_CONFIG` | Downward Dog (Adho Mukha Svanasana) | Hip 55°–100° | 30s |
462
+ | `COBRA_POSE_CONFIG` | Cobra Pose (Bhujangasana) | Hip extension 120°–170° | 30s |
463
+ | `TRIANGLE_POSE_CONFIG` | Triangle Pose (Trikonasana) | Front leg 160°–180° | 30s |
464
+ | `EXTENDED_SIDE_ANGLE_CONFIG` | Extended Side Angle (Utthita Parsvakonasana) | Front knee 80°–110° | 30s |
465
+ | `BRIDGE_POSE_CONFIG` | Bridge Pose (Setu Bandhasana) | Knee 80°–110° | 30s |
466
+ | `BOAT_POSE_CONFIG` | Boat Pose (Navasana) | Hip flexion 60°–110° | 30s |
467
+ | `CAMEL_POSE_CONFIG` | Camel Pose (Ustrasana) | Hip extension 120°–165° | 30s |
468
+ | `CHILDS_POSE_CONFIG` | Child's Pose (Balasana) | Hip fold 30°–80° | 60s |
469
+ | `BOW_POSE_CONFIG` | Bow Pose (Dhanurasana) | Knee 50°–100° | 30s |
470
+ | `FISH_POSE_CONFIG` | Fish Pose (Matsyasana) | Chest open 130°–170° | 30s |
452
471
 
453
472
  ### Custom Exercise Config
454
473
 
@@ -458,6 +477,7 @@ import type { ExerciseConfig } from 'react-native-nitro-pose-exercises';
458
477
  const MY_EXERCISE: ExerciseConfig = {
459
478
  name: 'Custom Exercise',
460
479
  type: 'rep', // 'rep' | 'hold'
480
+ postureFamily: 'standingUpright', // ← required: see table above
461
481
  angles: [
462
482
  { name: 'myAngle', landmarkA: 11, landmarkB: 13, landmarkC: 15 },
463
483
  ],
@@ -486,12 +506,24 @@ Landmarks are mapped to MediaPipe-compatible indices on both platforms. iOS Visi
486
506
  | 14 | Right elbow | 26 | Right knee |
487
507
  | 15 | Left wrist | 27 | Left ankle |
488
508
 
489
- **iOS note:** Vision provides 19 joints. Indices not available from Vision (face details 1-10, hands 17-22, feet 29-32) are filled with `visibility: 0` and skipped by the skeleton overlay.
509
+ **iOS note:** Vision provides 19 joints. Indices not available (face 1-10, hands 17-22, feet 29-32) are filled with `visibility: 0`.
490
510
 
491
511
  **Android note:** ML Kit provides all 33 landmarks matching MediaPipe indices exactly.
492
512
 
493
513
  ---
494
514
 
515
+ ## 📏 Camera Angle Guide
516
+
517
+ | ✅ Good | ❌ Bad |
518
+ | --- | --- |
519
+ | Side view, full body visible | Front-facing view |
520
+ | Phone at waist height, 6-8 ft away | Ground-level angle |
521
+ | Well-lit environment | Heavy glare or backlight |
522
+
523
+ 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.
524
+
525
+ ---
526
+
495
527
  ## 🏗️ Architecture — OS-Native vs MediaPipe
496
528
 
497
529
  | | OS-Native (current) | MediaPipe (previous) |
@@ -499,21 +531,24 @@ Landmarks are mapped to MediaPipe-compatible indices on both platforms. iOS Visi
499
531
  | **iOS** | Apple Vision framework (built-in) | MediaPipeTasksVision (CocoaPod) |
500
532
  | **Android** | Google ML Kit (Play Services) | com.google.mediapipe:tasks-vision |
501
533
  | **Model file** | None needed | ~3 MB bundled `.task` file |
502
- | **Color conversion** | None — takes CVPixelBuffer/ImageProxy directly | BGRA required (iOS), NV21→RGB (Android) |
534
+ | **Color conversion** | None — takes CVPixelBuffer/InputImage directly | BGRA required (iOS), NV21→RGB (Android) |
503
535
  | **App size impact** | ~200 KB (Nitro module code only) | ~11-15 MB (SDK + model) |
504
536
  | **Updates** | OS/Play Services updates | Manual model file replacement |
505
537
 
506
538
  ---
507
539
 
508
- ## 📏 Camera Angle Guide
509
-
510
- For best results, the camera should see the exerciser from a **side profile**:
540
+ ## 🛡️ Safety Features
511
541
 
512
- | Good | Bad |
542
+ | Feature | Description |
513
543
  | --- | --- |
514
- | Side view, full body visible | Front-facing view |
515
- | Phone at waist height, 6-8 ft away | Ground-level angle |
516
- | Well-lit environment | Heavy glare or backlight |
544
+ | **Min rep duration** | 800ms minimum per rep — prevents false counts from sensor noise |
545
+ | **Form score gate** | Reps with form score below 30/100 are rejected and not counted |
546
+ | **Feedback throttle** | Same form warning fires max once every 5 seconds to avoid UI spam |
547
+ | **Pose lost detection** | `onPoseLost` / `onPoseRegained` callbacks when user exits/enters frame |
548
+ | **Frame throttle** | Processes every 3rd frame to reduce CPU load without losing accuracy |
549
+ | **Visibility filter** | Landmarks with confidence below 0.3 are excluded from angle calculations |
550
+ | **Posture entry gate** | Sessions don't start counting until `isReady()` returns true |
551
+ | **Posture hysteresis** | 10 consecutive failed frames required to fire `onPostureLost` — prevents flicker |
517
552
 
518
553
  ---
519
554
 
@@ -521,8 +556,8 @@ For best results, the camera should see the exerciser from a **side profile**:
521
556
 
522
557
  | Platform | Status | Notes |
523
558
  | --- | --- | --- |
524
- | **iOS** | ✅ Supported | Requires physical device, iOS 14+ (Vision body pose) |
525
- | **Android** | ✅ Supported | API 23+ (ML Kit), Google Play Services required |
559
+ | **iOS** | ✅ Supported | Physical device, iOS 14+ (Vision body pose) |
560
+ | **Android** | ✅ Supported | API 23+, Google Play Services required |
526
561
  | **iOS Simulator** | ❌ Not supported | No camera access |
527
562
  | **Android Emulator** | ❌ Not supported | No real camera feed |
528
563
 
@@ -537,13 +572,11 @@ For best results, the camera should see the exerciser from a **side profile**:
537
572
  | Vision framework (iOS, built-in) | ~0 KB (system framework) |
538
573
  | **Total new addition** | **~200 KB** |
539
574
 
540
- Compared to the MediaPipe approach (~11-15 MB), the OS-native approach adds virtually zero app size.
541
-
542
575
  ---
543
576
 
544
577
  ## 🤝 Contributing
545
578
 
546
- PRs welcome!
579
+ PRs welcome! Adding a new exercise is as simple as creating a config file — no native code changes needed.
547
580
 
548
581
  * [Development Workflow](CONTRIBUTING.md#development-workflow)
549
582
  * [Sending a PR](CONTRIBUTING.md#sending-a-pull-request)