react-native-nitro-pose-exercises 1.0.8 → 1.0.9

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 (79) hide show
  1. package/android/build.gradle +9 -10
  2. package/android/src/main/java/com/margelo/nitro/nitroposeexercises/NitroPoseExercises.kt +73 -127
  3. package/lib/module/config/bicep-curl.js +61 -0
  4. package/lib/module/config/bicep-curl.js.map +1 -0
  5. package/lib/module/config/chair-pose.js +64 -0
  6. package/lib/module/config/chair-pose.js.map +1 -0
  7. package/lib/module/config/cobra-pose.js +57 -0
  8. package/lib/module/config/cobra-pose.js.map +1 -0
  9. package/lib/module/config/downward-dog.js +65 -0
  10. package/lib/module/config/downward-dog.js.map +1 -0
  11. package/lib/module/config/lunge.js +62 -0
  12. package/lib/module/config/lunge.js.map +1 -0
  13. package/lib/module/config/plank.js +57 -0
  14. package/lib/module/config/plank.js.map +1 -0
  15. package/lib/module/config/shoulder-press.js +62 -0
  16. package/lib/module/config/shoulder-press.js.map +1 -0
  17. package/lib/module/config/situp.js +48 -0
  18. package/lib/module/config/situp.js.map +1 -0
  19. package/lib/module/config/squat.js +69 -0
  20. package/lib/module/config/squat.js.map +1 -0
  21. package/lib/module/config/tree-pose.js +57 -0
  22. package/lib/module/config/tree-pose.js.map +1 -0
  23. package/lib/module/config/tricep-dip.js +55 -0
  24. package/lib/module/config/tricep-dip.js.map +1 -0
  25. package/lib/module/config/wall-sit.js +57 -0
  26. package/lib/module/config/wall-sit.js.map +1 -0
  27. package/lib/module/config/warrior-i.js +79 -0
  28. package/lib/module/config/warrior-i.js.map +1 -0
  29. package/lib/module/config/warrior-ii.js +86 -0
  30. package/lib/module/config/warrior-ii.js.map +1 -0
  31. package/lib/module/index.js +19 -0
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/typescript/src/config/bicep-curl.d.ts +3 -0
  34. package/lib/typescript/src/config/bicep-curl.d.ts.map +1 -0
  35. package/lib/typescript/src/config/chair-pose.d.ts +3 -0
  36. package/lib/typescript/src/config/chair-pose.d.ts.map +1 -0
  37. package/lib/typescript/src/config/cobra-pose.d.ts +3 -0
  38. package/lib/typescript/src/config/cobra-pose.d.ts.map +1 -0
  39. package/lib/typescript/src/config/downward-dog.d.ts +3 -0
  40. package/lib/typescript/src/config/downward-dog.d.ts.map +1 -0
  41. package/lib/typescript/src/config/lunge.d.ts +3 -0
  42. package/lib/typescript/src/config/lunge.d.ts.map +1 -0
  43. package/lib/typescript/src/config/plank.d.ts +3 -0
  44. package/lib/typescript/src/config/plank.d.ts.map +1 -0
  45. package/lib/typescript/src/config/shoulder-press.d.ts +3 -0
  46. package/lib/typescript/src/config/shoulder-press.d.ts.map +1 -0
  47. package/lib/typescript/src/config/situp.d.ts +3 -0
  48. package/lib/typescript/src/config/situp.d.ts.map +1 -0
  49. package/lib/typescript/src/config/squat.d.ts +3 -0
  50. package/lib/typescript/src/config/squat.d.ts.map +1 -0
  51. package/lib/typescript/src/config/tree-pose.d.ts +3 -0
  52. package/lib/typescript/src/config/tree-pose.d.ts.map +1 -0
  53. package/lib/typescript/src/config/tricep-dip.d.ts +3 -0
  54. package/lib/typescript/src/config/tricep-dip.d.ts.map +1 -0
  55. package/lib/typescript/src/config/wall-sit.d.ts +3 -0
  56. package/lib/typescript/src/config/wall-sit.d.ts.map +1 -0
  57. package/lib/typescript/src/config/warrior-i.d.ts +3 -0
  58. package/lib/typescript/src/config/warrior-i.d.ts.map +1 -0
  59. package/lib/typescript/src/config/warrior-ii.d.ts +3 -0
  60. package/lib/typescript/src/config/warrior-ii.d.ts.map +1 -0
  61. package/lib/typescript/src/index.d.ts +14 -0
  62. package/lib/typescript/src/index.d.ts.map +1 -1
  63. package/nitro.json +8 -1
  64. package/package.json +1 -1
  65. package/src/config/bicep-curl.ts +64 -0
  66. package/src/config/chair-pose.ts +67 -0
  67. package/src/config/cobra-pose.ts +59 -0
  68. package/src/config/downward-dog.ts +68 -0
  69. package/src/config/lunge.ts +65 -0
  70. package/src/config/plank.ts +59 -0
  71. package/src/config/shoulder-press.ts +63 -0
  72. package/src/config/situp.ts +51 -0
  73. package/src/config/squat.ts +71 -0
  74. package/src/config/tree-pose.ts +59 -0
  75. package/src/config/tricep-dip.ts +58 -0
  76. package/src/config/wall-sit.ts +59 -0
  77. package/src/config/warrior-i.ts +82 -0
  78. package/src/config/warrior-ii.ts +88 -0
  79. package/src/index.tsx +19 -0
@@ -1,7 +1,7 @@
1
1
  buildscript {
2
2
  ext.NitroPoseExercises = [
3
3
  kotlinVersion: "2.0.21",
4
- minSdkVersion: 24,
4
+ minSdkVersion: 26,
5
5
  compileSdkVersion: 36,
6
6
  targetSdkVersion: 36
7
7
  ]
@@ -112,16 +112,15 @@ android {
112
112
  }
113
113
  }
114
114
 
115
+ repositories {
116
+ google()
117
+ mavenCentral()
118
+ }
119
+
115
120
  dependencies {
116
121
  implementation "com.facebook.react:react-android"
117
122
  implementation project(":react-native-nitro-modules")
118
-
119
- // MediaPipe — bundled with your library
120
- implementation 'com.google.mediapipe:tasks-vision:0.10.21'
121
-
122
- // CameraX for ImageProxy access
123
- implementation 'androidx.camera:camera-core:1.4.1'
124
-
125
- // VisionCamera — provided by the consuming app
126
- compileOnly project(':react-native-vision-camera')
123
+ implementation 'com.google.mediapipe:tasks-vision:0.10.29'
124
+ implementation project(":react-native-vision-camera")
127
125
  }
126
+
@@ -7,10 +7,11 @@ import com.google.mediapipe.framework.image.BitmapImageBuilder
7
7
  import com.google.mediapipe.tasks.core.BaseOptions
8
8
  import com.google.mediapipe.tasks.vision.core.RunningMode
9
9
  import com.google.mediapipe.tasks.vision.poselandmarker.PoseLandmarker
10
- import com.google.mediapipe.tasks.vision.poselandmarker.PoseLandmarkerOptions
10
+ import com.google.mediapipe.tasks.vision.poselandmarker.PoseLandmarker.PoseLandmarkerOptions
11
11
  import com.margelo.nitro.NitroModules
12
12
  import com.margelo.nitro.core.Promise
13
13
  import com.margelo.nitro.camera.HybridFrameSpec
14
+ import com.margelo.nitro.camera.public.NativeFrame
14
15
  import kotlin.math.acos
15
16
  import kotlin.math.max
16
17
  import kotlin.math.min
@@ -18,7 +19,7 @@ import kotlin.math.sqrt
18
19
 
19
20
  @Keep
20
21
  @DoNotStrip
21
- class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
22
+ class NitroPoseExercises : HybridNitroPoseExercisesSpec() {
22
23
 
23
24
  // ─── MediaPipe ──────────────────────────────────────────────
24
25
  private var poseLandmarker: PoseLandmarker? = null
@@ -46,8 +47,6 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
46
47
  private var sessionStartTime: Long = System.currentTimeMillis()
47
48
  private var targetReps: Double = 0.0
48
49
  private var countdownSeconds: Double = 0.0
49
- private var frameCount: Int = 0
50
- private val processEveryNFrames: Int = 3
51
50
 
52
51
  // ─── Form Tracking ──────────────────────────────────────────
53
52
  private var lastFormFeedbackTime = mutableMapOf<String, Long>()
@@ -60,6 +59,10 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
60
59
  // ─── Pose Tracking ──────────────────────────────────────────
61
60
  private var poseWasLost = false
62
61
 
62
+ // ─── Frame Skip ─────────────────────────────────────────────
63
+ private var frameCount: Int = 0
64
+ private val processEveryNFrames: Int = 3
65
+
63
66
  // ─── Callbacks ──────────────────────────────────────────────
64
67
  override var onRepComplete: ((data: RepData) -> Unit)? = null
65
68
  override var onPhaseChange: ((phase: ExercisePhase) -> Unit)? = null
@@ -154,7 +157,7 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
154
157
  // Frame Processing
155
158
  // ═══════════════════════════════════════════════════════════
156
159
 
157
- override fun processFrame(frame: HybridFrameSpec) {
160
+ override fun processFrame(frame: HybridFrameSpec) {
158
161
  if (_status != SessionStatus.ACTIVE && _status != SessionStatus.COUNTDOWN) return
159
162
  if (!isInitialized || poseLandmarker == null) return
160
163
 
@@ -162,94 +165,50 @@ override fun processFrame(frame: HybridFrameSpec) {
162
165
  if (frameCount % processEveryNFrames != 0) return
163
166
 
164
167
  try {
165
- val nativeBuffer = frame.getNativeBuffer()
166
- val width = frame.getWidth().toInt()
167
- val height = frame.getHeight().toInt()
168
-
169
- // On Android, nativeBuffer.pointer is an AHardwareBuffer*
170
- // Convert to Bitmap via Android's HardwareBuffer API
171
- val hardwareBuffer = android.hardware.HardwareBuffer.wrap(nativeBuffer.pointer)
172
- val bitmap = Bitmap.wrapHardwareBuffer(hardwareBuffer, null)
173
- ?: throw Exception("Failed to wrap HardwareBuffer to Bitmap")
174
-
175
- // MediaPipe needs ARGB_8888, not hardware bitmap
176
- val softBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false)
177
-
178
- val mpImage = com.google.mediapipe.framework.image.BitmapImageBuilder(softBitmap).build()
179
- val result = poseLandmarker!!.detect(mpImage)
180
-
181
- if (result.landmarks().isNotEmpty()) {
182
- val poseLandmarks = result.landmarks()[0]
183
-
184
- if (poseWasLost) {
185
- poseWasLost = false
186
- onPoseRegained?.invoke()
187
- }
188
-
189
- _landmarks = poseLandmarks.map { lm ->
190
- Landmark(
191
- x = lm.x().toDouble(),
192
- y = lm.y().toDouble(),
193
- z = lm.z().toDouble(),
194
- visibility = (lm.visibility().orElse(0f)).toDouble()
195
- )
196
- }.toTypedArray()
197
-
198
- if (_status == SessionStatus.ACTIVE) {
199
- processExerciseLogic()
200
- }
201
- } else {
202
- if (!poseWasLost) {
203
- poseWasLost = true
204
- onPoseLost?.invoke()
205
- }
206
- _landmarks = emptyArray()
168
+ // Get the native buffer from VisionCamera frame
169
+ val nativeBuffer = frame.getNativeBuffer()
170
+ val w = frame.width.toInt()
171
+ val h = frame.height.toInt()
172
+
173
+ // On Android, the pointer is an AHardwareBuffer*
174
+ // Create a Bitmap and use MediaPipe's BitmapImageBuilder
175
+ val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
176
+ val mpImage = BitmapImageBuilder(bitmap).build()
177
+ val result = poseLandmarker!!.detect(mpImage)
178
+
179
+ if (result.landmarks().isNotEmpty()) {
180
+ val poseLandmarks = result.landmarks()[0]
181
+
182
+ if (poseWasLost) {
183
+ poseWasLost = false
184
+ onPoseRegained?.invoke()
207
185
  }
208
186
 
209
- softBitmap.recycle()
210
- bitmap.recycle()
211
- nativeBuffer.release()
212
-
213
- } catch (e: Exception) {
214
- // MediaPipe detection failed — skip this frame
215
- }
216
- }
187
+ _landmarks = poseLandmarks.map { lm ->
188
+ Landmark(
189
+ x = lm.x().toDouble(),
190
+ y = lm.y().toDouble(),
191
+ z = lm.z().toDouble(),
192
+ visibility = (lm.visibility().orElse(0f)).toDouble()
193
+ )
194
+ }.toTypedArray()
195
+
196
+ if (_status == SessionStatus.ACTIVE) {
197
+ processExerciseLogic()
198
+ }
199
+ } else {
200
+ if (!poseWasLost) {
201
+ poseWasLost = true
202
+ onPoseLost?.invoke()
203
+ }
204
+ _landmarks = emptyArray()
205
+ }
217
206
 
218
- // ═══════════════════════════════════════════════════════════
219
- // ImageProxy to Bitmap conversion
220
- // ═══════════════════════════════════════════════════════════
207
+ bitmap.recycle()
208
+ nativeBuffer.release()
221
209
 
222
- private fun imageProxyToBitmap(imageProxy: androidx.camera.core.ImageProxy): Bitmap {
223
- val buffer = imageProxy.planes[0].buffer
224
- val bytes = ByteArray(buffer.remaining())
225
- buffer.get(bytes)
226
-
227
- val yuvImage = android.graphics.YuvImage(
228
- bytes,
229
- android.graphics.ImageFormat.NV21,
230
- imageProxy.width,
231
- imageProxy.height,
232
- null
233
- )
234
-
235
- val out = java.io.ByteArrayOutputStream()
236
- yuvImage.compressToJpeg(
237
- android.graphics.Rect(0, 0, imageProxy.width, imageProxy.height),
238
- 100,
239
- out
240
- )
241
-
242
- val jpegBytes = out.toByteArray()
243
- val bitmap = android.graphics.BitmapFactory.decodeByteArray(jpegBytes, 0, jpegBytes.size)
244
-
245
- // Apply rotation if needed
246
- val rotation = imageProxy.imageInfo.rotationDegrees
247
- return if (rotation != 0) {
248
- val matrix = Matrix()
249
- matrix.postRotate(rotation.toFloat())
250
- Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
251
- } else {
252
- bitmap
210
+ } catch (e: Exception) {
211
+ // MediaPipe detection failed — skip this frame
253
212
  }
254
213
  }
255
214
 
@@ -261,7 +220,6 @@ override fun processFrame(frame: HybridFrameSpec) {
261
220
  val config = exerciseConfig ?: return
262
221
  if (_landmarks.isEmpty()) return
263
222
 
264
- // 1. Calculate all angles
265
223
  val currentAngles = mutableMapOf<String, Double>()
266
224
  val angleSnapshots = mutableListOf<AngleSnapshot>()
267
225
 
@@ -279,7 +237,6 @@ override fun processFrame(frame: HybridFrameSpec) {
279
237
 
280
238
  repAngleSnapshots = angleSnapshots.toTypedArray()
281
239
 
282
- // 2. Determine current phase
283
240
  val detectedPhase = determinePhase(currentAngles, config)
284
241
 
285
242
  if (detectedPhase != _currentPhase && detectedPhase != ExercisePhase.UNKNOWN) {
@@ -288,10 +245,8 @@ override fun processFrame(frame: HybridFrameSpec) {
288
245
  handlePhaseTransition(detectedPhase, config)
289
246
  }
290
247
 
291
- // 3. Check form rules
292
248
  checkFormRules(currentAngles, config)
293
249
 
294
- // 4. Handle hold-based exercises
295
250
  if (config.type == ExerciseType.HOLD) {
296
251
  handleHoldProgress(currentAngles, config)
297
252
  }
@@ -345,37 +300,37 @@ override fun processFrame(frame: HybridFrameSpec) {
345
300
  if (phaseHistory.size >= repSeq.size) {
346
301
  val tail = phaseHistory.takeLast(repSeq.size)
347
302
 
348
- if (tail == repSeq.toList()) {
303
+ if (tail == repSeq.toList()) {
349
304
  val now = System.currentTimeMillis()
350
305
  val repDuration = (now - repStartTime).toDouble()
351
306
 
352
307
  // Minimum 800ms per rep
353
308
  if (repDuration < 800) {
354
- phaseHistory.clear()
355
- phaseHistory.add(newPhase)
356
- return
309
+ phaseHistory.clear()
310
+ phaseHistory.add(newPhase)
311
+ return
357
312
  }
358
313
 
359
314
  // Don't count rep if form is terrible
360
315
  if (repFormScore <= 30) {
361
- onFormFeedback?.invoke(FormFeedback(
362
- ruleName = "poorForm",
363
- message = "Fix your form before continuing",
364
- severity = FormSeverity.ERROR
365
- ))
366
- repFormScore = 100.0
367
- phaseHistory.clear()
368
- phaseHistory.add(newPhase)
369
- return
316
+ onFormFeedback?.invoke(FormFeedback(
317
+ ruleName = "poorForm",
318
+ message = "Fix your form before continuing",
319
+ severity = FormSeverity.ERROR
320
+ ))
321
+ repFormScore = 100.0
322
+ phaseHistory.clear()
323
+ phaseHistory.add(newPhase)
324
+ return
370
325
  }
371
326
 
372
327
  _repCount += 1.0
373
328
 
374
329
  val repData = RepData(
375
- repNumber = _repCount,
376
- durationMs = repDuration,
377
- formScore = repFormScore,
378
- angles = repAngleSnapshots
330
+ repNumber = _repCount,
331
+ durationMs = repDuration,
332
+ formScore = repFormScore,
333
+ angles = repAngleSnapshots
379
334
  )
380
335
 
381
336
  allRepDurations.add(repDuration)
@@ -389,9 +344,9 @@ override fun processFrame(frame: HybridFrameSpec) {
389
344
  phaseHistory.add(newPhase)
390
345
 
391
346
  if (targetReps > 0 && _repCount >= targetReps) {
392
- completeSession()
347
+ completeSession()
393
348
  }
394
- }
349
+ }
395
350
  }
396
351
 
397
352
  val maxHistory = repSeq.size * 2
@@ -410,7 +365,6 @@ override fun processFrame(frame: HybridFrameSpec) {
410
365
 
411
366
  for (rule in config.formRules) {
412
367
  val angle = currentAngles[rule.angleName] ?: continue
413
-
414
368
  val isViolating = angle < rule.minAngle || angle > rule.maxAngle
415
369
 
416
370
  if (isViolating) {
@@ -453,24 +407,18 @@ override fun processFrame(frame: HybridFrameSpec) {
453
407
  }
454
408
 
455
409
  if (inPosition) {
456
- if (holdStartTime == null) {
457
- holdStartTime = System.currentTimeMillis()
458
- }
410
+ if (holdStartTime == null) holdStartTime = System.currentTimeMillis()
459
411
 
460
412
  val elapsed = (System.currentTimeMillis() - holdStartTime!!).toDouble()
461
413
  val stability = min(100.0, max(0.0, repFormScore))
462
414
 
463
- val progress = HoldProgress(
415
+ onHoldProgress?.invoke(HoldProgress(
464
416
  elapsedMs = elapsed,
465
417
  targetMs = config.holdDurationMs,
466
418
  stability = stability
467
- )
468
-
469
- onHoldProgress?.invoke(progress)
419
+ ))
470
420
 
471
- if (elapsed >= config.holdDurationMs) {
472
- completeSession()
473
- }
421
+ if (elapsed >= config.holdDurationMs) completeSession()
474
422
  } else {
475
423
  holdStartTime = null
476
424
  }
@@ -487,16 +435,14 @@ override fun processFrame(frame: HybridFrameSpec) {
487
435
  val avgRepDuration = if (allRepDurations.isEmpty()) 0.0 else allRepDurations.average()
488
436
  val avgFormScore = if (allRepFormScores.isEmpty()) 100.0 else allRepFormScores.average()
489
437
 
490
- val result = SessionResult(
438
+ onSessionComplete?.invoke(SessionResult(
491
439
  totalReps = _repCount,
492
440
  totalDurationMs = totalDuration,
493
441
  averageRepDurationMs = avgRepDuration,
494
442
  averageFormScore = avgFormScore,
495
443
  formViolations = sessionFormViolations.toTypedArray(),
496
444
  angleHistory = repAngleSnapshots
497
- )
498
-
499
- onSessionComplete?.invoke(result)
445
+ ))
500
446
  }
501
447
 
502
448
  // ═══════════════════════════════════════════════════════════
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ // MediaPipe Pose Landmark indices
4
+ // 11 = left shoulder, 13 = left elbow, 15 = left wrist
5
+ // 12 = right shoulder, 14 = right elbow, 16 = right wrist
6
+
7
+ export const BICEP_CURL_CONFIG = {
8
+ name: 'Bicep Curl',
9
+ type: 'rep',
10
+ angles: [{
11
+ name: 'leftElbow',
12
+ landmarkA: 11,
13
+ // left shoulder
14
+ landmarkB: 13,
15
+ // left elbow (vertex)
16
+ landmarkC: 15 // left wrist
17
+ }, {
18
+ name: 'rightElbow',
19
+ landmarkA: 12,
20
+ // right shoulder
21
+ landmarkB: 14,
22
+ // right elbow (vertex)
23
+ landmarkC: 16 // right wrist
24
+ }, {
25
+ name: 'leftShoulder',
26
+ landmarkA: 23,
27
+ // left hip
28
+ landmarkB: 11,
29
+ // left shoulder (vertex)
30
+ landmarkC: 13 // left elbow
31
+ }],
32
+ phases: [{
33
+ phase: 'down',
34
+ angleName: 'leftElbow',
35
+ minAngle: 150,
36
+ maxAngle: 180
37
+ }, {
38
+ phase: 'up',
39
+ angleName: 'leftElbow',
40
+ minAngle: 25,
41
+ maxAngle: 70
42
+ }],
43
+ repSequence: ['down', 'up', 'down'],
44
+ formRules: [{
45
+ name: 'elbowFlare',
46
+ message: 'Keep your elbows pinned to your sides',
47
+ severity: 'warning',
48
+ angleName: 'leftShoulder',
49
+ minAngle: 0,
50
+ maxAngle: 30
51
+ }, {
52
+ name: 'swinging',
53
+ message: "Don't swing — control the movement",
54
+ severity: 'error',
55
+ angleName: 'leftShoulder',
56
+ minAngle: 0,
57
+ maxAngle: 45
58
+ }],
59
+ holdDurationMs: 0
60
+ };
61
+ //# sourceMappingURL=bicep-curl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["BICEP_CURL_CONFIG","name","type","angles","landmarkA","landmarkB","landmarkC","phases","phase","angleName","minAngle","maxAngle","repSequence","formRules","message","severity","holdDurationMs"],"sourceRoot":"../../../src","sources":["config/bicep-curl.ts"],"mappings":";;AAEA;AACA;AACA;;AAEA,OAAO,MAAMA,iBAAiC,GAAG;EAC/CC,IAAI,EAAE,YAAY;EAClBC,IAAI,EAAE,KAAK;EACXC,MAAM,EAAE,CACN;IACEF,IAAI,EAAE,WAAW;IACjBG,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE,CAAE;EACjB,CAAC,EACD;IACEL,IAAI,EAAE,YAAY;IAClBG,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE,CAAE;EACjB,CAAC,EACD;IACEL,IAAI,EAAE,cAAc;IACpBG,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE,CAAE;EACjB,CAAC,CACF;EACDC,MAAM,EAAE,CACN;IACEC,KAAK,EAAE,MAAM;IACbC,SAAS,EAAE,WAAW;IACtBC,QAAQ,EAAE,GAAG;IACbC,QAAQ,EAAE;EACZ,CAAC,EACD;IACEH,KAAK,EAAE,IAAI;IACXC,SAAS,EAAE,WAAW;IACtBC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE;EACZ,CAAC,CACF;EACDC,WAAW,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC;EACnCC,SAAS,EAAE,CACT;IACEZ,IAAI,EAAE,YAAY;IAClBa,OAAO,EAAE,uCAAuC;IAChDC,QAAQ,EAAE,SAAS;IACnBN,SAAS,EAAE,cAAc;IACzBC,QAAQ,EAAE,CAAC;IACXC,QAAQ,EAAE;EACZ,CAAC,EACD;IACEV,IAAI,EAAE,UAAU;IAChBa,OAAO,EAAE,oCAAoC;IAC7CC,QAAQ,EAAE,OAAO;IACjBN,SAAS,EAAE,cAAc;IACzBC,QAAQ,EAAE,CAAC;IACXC,QAAQ,EAAE;EACZ,CAAC,CACF;EACDK,cAAc,EAAE;AAClB,CAAC","ignoreList":[]}
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+
3
+ // MediaPipe Pose Landmark indices
4
+ // 11 = left shoulder, 13 = left elbow, 15 = left wrist
5
+ // 23 = left hip, 25 = left knee, 27 = left ankle
6
+ // 12 = right shoulder, 24 = right hip
7
+
8
+ export const CHAIR_POSE_CONFIG = {
9
+ name: 'Chair Pose (Utkatasana)',
10
+ type: 'hold',
11
+ angles: [{
12
+ name: 'leftKnee',
13
+ landmarkA: 23,
14
+ // left hip
15
+ landmarkB: 25,
16
+ // left knee (vertex)
17
+ landmarkC: 27 // left ankle
18
+ }, {
19
+ name: 'leftHip',
20
+ landmarkA: 11,
21
+ // left shoulder
22
+ landmarkB: 23,
23
+ // left hip (vertex)
24
+ landmarkC: 25 // left knee
25
+ }, {
26
+ name: 'leftArm',
27
+ landmarkA: 11,
28
+ // left shoulder
29
+ landmarkB: 13,
30
+ // left elbow (vertex)
31
+ landmarkC: 15 // left wrist
32
+ }],
33
+ phases: [{
34
+ phase: 'hold',
35
+ angleName: 'leftKnee',
36
+ minAngle: 90,
37
+ maxAngle: 130
38
+ }],
39
+ repSequence: [],
40
+ formRules: [{
41
+ name: 'kneesTooStraight',
42
+ message: 'Sit deeper — bend your knees more',
43
+ severity: 'warning',
44
+ angleName: 'leftKnee',
45
+ minAngle: 90,
46
+ maxAngle: 130
47
+ }, {
48
+ name: 'leaningForward',
49
+ message: "Keep your chest lifted — don't lean too far forward",
50
+ severity: 'warning',
51
+ angleName: 'leftHip',
52
+ minAngle: 70,
53
+ maxAngle: 130
54
+ }, {
55
+ name: 'armsNotUp',
56
+ message: 'Reach your arms up overhead',
57
+ severity: 'info',
58
+ angleName: 'leftArm',
59
+ minAngle: 160,
60
+ maxAngle: 180
61
+ }],
62
+ holdDurationMs: 30000 // 30 seconds
63
+ };
64
+ //# sourceMappingURL=chair-pose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["CHAIR_POSE_CONFIG","name","type","angles","landmarkA","landmarkB","landmarkC","phases","phase","angleName","minAngle","maxAngle","repSequence","formRules","message","severity","holdDurationMs"],"sourceRoot":"../../../src","sources":["config/chair-pose.ts"],"mappings":";;AAEA;AACA;AACA;AACA;;AAEA,OAAO,MAAMA,iBAAiC,GAAG;EAC/CC,IAAI,EAAE,yBAAyB;EAC/BC,IAAI,EAAE,MAAM;EACZC,MAAM,EAAE,CACN;IACEF,IAAI,EAAE,UAAU;IAChBG,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE,CAAE;EACjB,CAAC,EACD;IACEL,IAAI,EAAE,SAAS;IACfG,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE,CAAE;EACjB,CAAC,EACD;IACEL,IAAI,EAAE,SAAS;IACfG,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE,CAAE;EACjB,CAAC,CACF;EACDC,MAAM,EAAE,CACN;IACEC,KAAK,EAAE,MAAM;IACbC,SAAS,EAAE,UAAU;IACrBC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE;EACZ,CAAC,CACF;EACDC,WAAW,EAAE,EAAE;EACfC,SAAS,EAAE,CACT;IACEZ,IAAI,EAAE,kBAAkB;IACxBa,OAAO,EAAE,mCAAmC;IAC5CC,QAAQ,EAAE,SAAS;IACnBN,SAAS,EAAE,UAAU;IACrBC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE;EACZ,CAAC,EACD;IACEV,IAAI,EAAE,gBAAgB;IACtBa,OAAO,EAAE,qDAAqD;IAC9DC,QAAQ,EAAE,SAAS;IACnBN,SAAS,EAAE,SAAS;IACpBC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE;EACZ,CAAC,EACD;IACEV,IAAI,EAAE,WAAW;IACjBa,OAAO,EAAE,6BAA6B;IACtCC,QAAQ,EAAE,MAAM;IAChBN,SAAS,EAAE,SAAS;IACpBC,QAAQ,EAAE,GAAG;IACbC,QAAQ,EAAE;EACZ,CAAC,CACF;EACDK,cAAc,EAAE,KAAK,CAAE;AACzB,CAAC","ignoreList":[]}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+
3
+ // MediaPipe Pose Landmark indices
4
+ // 11 = left shoulder, 13 = left elbow, 15 = left wrist
5
+ // 12 = right shoulder, 14 = right elbow, 16 = right wrist
6
+ // 23 = left hip, 25 = left knee, 27 = left ankle
7
+
8
+ export const COBRA_POSE_CONFIG = {
9
+ name: 'Cobra Pose (Bhujangasana)',
10
+ type: 'hold',
11
+ angles: [{
12
+ name: 'leftElbow',
13
+ landmarkA: 11,
14
+ // left shoulder
15
+ landmarkB: 13,
16
+ // left elbow (vertex)
17
+ landmarkC: 15 // left wrist
18
+ }, {
19
+ name: 'hipExtension',
20
+ landmarkA: 11,
21
+ // left shoulder
22
+ landmarkB: 23,
23
+ // left hip (vertex)
24
+ landmarkC: 25 // left knee
25
+ }, {
26
+ name: 'legStraight',
27
+ landmarkA: 23,
28
+ // left hip
29
+ landmarkB: 25,
30
+ // left knee (vertex)
31
+ landmarkC: 27 // left ankle
32
+ }],
33
+ phases: [{
34
+ phase: 'hold',
35
+ angleName: 'hipExtension',
36
+ minAngle: 120,
37
+ maxAngle: 170
38
+ }],
39
+ repSequence: [],
40
+ formRules: [{
41
+ name: 'shouldersTensed',
42
+ message: 'Relax your shoulders away from your ears',
43
+ severity: 'info',
44
+ angleName: 'leftElbow',
45
+ minAngle: 140,
46
+ maxAngle: 180
47
+ }, {
48
+ name: 'legsBending',
49
+ message: 'Keep your legs straight and pressed into the floor',
50
+ severity: 'warning',
51
+ angleName: 'legStraight',
52
+ minAngle: 160,
53
+ maxAngle: 180
54
+ }],
55
+ holdDurationMs: 30000 // 30 seconds
56
+ };
57
+ //# sourceMappingURL=cobra-pose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["COBRA_POSE_CONFIG","name","type","angles","landmarkA","landmarkB","landmarkC","phases","phase","angleName","minAngle","maxAngle","repSequence","formRules","message","severity","holdDurationMs"],"sourceRoot":"../../../src","sources":["config/cobra-pose.ts"],"mappings":";;AAEA;AACA;AACA;AACA;;AAEA,OAAO,MAAMA,iBAAiC,GAAG;EAC/CC,IAAI,EAAE,2BAA2B;EACjCC,IAAI,EAAE,MAAM;EACZC,MAAM,EAAE,CACN;IACEF,IAAI,EAAE,WAAW;IACjBG,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE,CAAE;EACjB,CAAC,EACD;IACEL,IAAI,EAAE,cAAc;IACpBG,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE,CAAE;EACjB,CAAC,EACD;IACEL,IAAI,EAAE,aAAa;IACnBG,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE,CAAE;EACjB,CAAC,CACF;EACDC,MAAM,EAAE,CACN;IACEC,KAAK,EAAE,MAAM;IACbC,SAAS,EAAE,cAAc;IACzBC,QAAQ,EAAE,GAAG;IACbC,QAAQ,EAAE;EACZ,CAAC,CACF;EACDC,WAAW,EAAE,EAAE;EACfC,SAAS,EAAE,CACT;IACEZ,IAAI,EAAE,iBAAiB;IACvBa,OAAO,EAAE,0CAA0C;IACnDC,QAAQ,EAAE,MAAM;IAChBN,SAAS,EAAE,WAAW;IACtBC,QAAQ,EAAE,GAAG;IACbC,QAAQ,EAAE;EACZ,CAAC,EACD;IACEV,IAAI,EAAE,aAAa;IACnBa,OAAO,EAAE,oDAAoD;IAC7DC,QAAQ,EAAE,SAAS;IACnBN,SAAS,EAAE,aAAa;IACxBC,QAAQ,EAAE,GAAG;IACbC,QAAQ,EAAE;EACZ,CAAC,CACF;EACDK,cAAc,EAAE,KAAK,CAAE;AACzB,CAAC","ignoreList":[]}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+
3
+ // MediaPipe Pose Landmark indices
4
+ // 11 = left shoulder, 13 = left elbow, 15 = left wrist
5
+ // 12 = right shoulder, 14 = right elbow, 16 = right wrist
6
+ // 23 = left hip, 25 = left knee, 27 = left ankle
7
+ // 24 = right hip, 26 = right knee, 28 = right ankle
8
+
9
+ export const DOWNWARD_DOG_CONFIG = {
10
+ name: 'Downward Dog (Adho Mukha Svanasana)',
11
+ type: 'hold',
12
+ angles: [{
13
+ name: 'hipAngle',
14
+ landmarkA: 11,
15
+ // left shoulder
16
+ landmarkB: 23,
17
+ // left hip (vertex)
18
+ landmarkC: 27 // left ankle
19
+ }, {
20
+ name: 'leftArm',
21
+ landmarkA: 11,
22
+ // left shoulder
23
+ landmarkB: 13,
24
+ // left elbow (vertex)
25
+ landmarkC: 15 // left wrist
26
+ }, {
27
+ name: 'leftLeg',
28
+ landmarkA: 23,
29
+ // left hip
30
+ landmarkB: 25,
31
+ // left knee (vertex)
32
+ landmarkC: 27 // left ankle
33
+ }],
34
+ phases: [{
35
+ phase: 'hold',
36
+ angleName: 'hipAngle',
37
+ minAngle: 55,
38
+ maxAngle: 100
39
+ }],
40
+ repSequence: [],
41
+ formRules: [{
42
+ name: 'armsBent',
43
+ message: 'Straighten your arms — push the floor away',
44
+ severity: 'warning',
45
+ angleName: 'leftArm',
46
+ minAngle: 160,
47
+ maxAngle: 180
48
+ }, {
49
+ name: 'legsBent',
50
+ message: 'Straighten your legs — press your heels toward the ground',
51
+ severity: 'info',
52
+ angleName: 'leftLeg',
53
+ minAngle: 155,
54
+ maxAngle: 180
55
+ }, {
56
+ name: 'hipsTooLow',
57
+ message: 'Push your hips up higher toward the ceiling',
58
+ severity: 'warning',
59
+ angleName: 'hipAngle',
60
+ minAngle: 55,
61
+ maxAngle: 100
62
+ }],
63
+ holdDurationMs: 30000 // 30 seconds
64
+ };
65
+ //# sourceMappingURL=downward-dog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["DOWNWARD_DOG_CONFIG","name","type","angles","landmarkA","landmarkB","landmarkC","phases","phase","angleName","minAngle","maxAngle","repSequence","formRules","message","severity","holdDurationMs"],"sourceRoot":"../../../src","sources":["config/downward-dog.ts"],"mappings":";;AAEA;AACA;AACA;AACA;AACA;;AAEA,OAAO,MAAMA,mBAAmC,GAAG;EACjDC,IAAI,EAAE,qCAAqC;EAC3CC,IAAI,EAAE,MAAM;EACZC,MAAM,EAAE,CACN;IACEF,IAAI,EAAE,UAAU;IAChBG,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE,CAAE;EACjB,CAAC,EACD;IACEL,IAAI,EAAE,SAAS;IACfG,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE,CAAE;EACjB,CAAC,EACD;IACEL,IAAI,EAAE,SAAS;IACfG,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE;IAAE;IACfC,SAAS,EAAE,EAAE,CAAE;EACjB,CAAC,CACF;EACDC,MAAM,EAAE,CACN;IACEC,KAAK,EAAE,MAAM;IACbC,SAAS,EAAE,UAAU;IACrBC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE;EACZ,CAAC,CACF;EACDC,WAAW,EAAE,EAAE;EACfC,SAAS,EAAE,CACT;IACEZ,IAAI,EAAE,UAAU;IAChBa,OAAO,EAAE,4CAA4C;IACrDC,QAAQ,EAAE,SAAS;IACnBN,SAAS,EAAE,SAAS;IACpBC,QAAQ,EAAE,GAAG;IACbC,QAAQ,EAAE;EACZ,CAAC,EACD;IACEV,IAAI,EAAE,UAAU;IAChBa,OAAO,EAAE,2DAA2D;IACpEC,QAAQ,EAAE,MAAM;IAChBN,SAAS,EAAE,SAAS;IACpBC,QAAQ,EAAE,GAAG;IACbC,QAAQ,EAAE;EACZ,CAAC,EACD;IACEV,IAAI,EAAE,YAAY;IAClBa,OAAO,EAAE,6CAA6C;IACtDC,QAAQ,EAAE,SAAS;IACnBN,SAAS,EAAE,UAAU;IACrBC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE;EACZ,CAAC,CACF;EACDK,cAAc,EAAE,KAAK,CAAE;AACzB,CAAC","ignoreList":[]}