react-native-nitro-pose-exercises 1.0.8 → 1.1.1
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/NitroPoseExercises.podspec +2 -1
- package/README.md +137 -92
- package/android/build.gradle +9 -10
- package/android/src/main/java/com/margelo/nitro/nitroposeexercises/NitroPoseExercises.kt +148 -125
- package/ios/NitroPoseExercises.swift +113 -88
- package/lib/module/config/bicep-curl.js +61 -0
- package/lib/module/config/bicep-curl.js.map +1 -0
- package/lib/module/config/chair-pose.js +64 -0
- package/lib/module/config/chair-pose.js.map +1 -0
- package/lib/module/config/cobra-pose.js +57 -0
- package/lib/module/config/cobra-pose.js.map +1 -0
- package/lib/module/config/downward-dog.js +65 -0
- package/lib/module/config/downward-dog.js.map +1 -0
- package/lib/module/config/lunge.js +62 -0
- package/lib/module/config/lunge.js.map +1 -0
- package/lib/module/config/plank.js +57 -0
- package/lib/module/config/plank.js.map +1 -0
- package/lib/module/config/shoulder-press.js +62 -0
- package/lib/module/config/shoulder-press.js.map +1 -0
- package/lib/module/config/situp.js +48 -0
- package/lib/module/config/situp.js.map +1 -0
- package/lib/module/config/squat.js +69 -0
- package/lib/module/config/squat.js.map +1 -0
- package/lib/module/config/tree-pose.js +57 -0
- package/lib/module/config/tree-pose.js.map +1 -0
- package/lib/module/config/tricep-dip.js +55 -0
- package/lib/module/config/tricep-dip.js.map +1 -0
- package/lib/module/config/wall-sit.js +57 -0
- package/lib/module/config/wall-sit.js.map +1 -0
- package/lib/module/config/warrior-i.js +79 -0
- package/lib/module/config/warrior-i.js.map +1 -0
- package/lib/module/config/warrior-ii.js +86 -0
- package/lib/module/config/warrior-ii.js.map +1 -0
- package/lib/module/index.js +19 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/config/bicep-curl.d.ts +3 -0
- package/lib/typescript/src/config/bicep-curl.d.ts.map +1 -0
- package/lib/typescript/src/config/chair-pose.d.ts +3 -0
- package/lib/typescript/src/config/chair-pose.d.ts.map +1 -0
- package/lib/typescript/src/config/cobra-pose.d.ts +3 -0
- package/lib/typescript/src/config/cobra-pose.d.ts.map +1 -0
- package/lib/typescript/src/config/downward-dog.d.ts +3 -0
- package/lib/typescript/src/config/downward-dog.d.ts.map +1 -0
- package/lib/typescript/src/config/lunge.d.ts +3 -0
- package/lib/typescript/src/config/lunge.d.ts.map +1 -0
- package/lib/typescript/src/config/plank.d.ts +3 -0
- package/lib/typescript/src/config/plank.d.ts.map +1 -0
- package/lib/typescript/src/config/shoulder-press.d.ts +3 -0
- package/lib/typescript/src/config/shoulder-press.d.ts.map +1 -0
- package/lib/typescript/src/config/situp.d.ts +3 -0
- package/lib/typescript/src/config/situp.d.ts.map +1 -0
- package/lib/typescript/src/config/squat.d.ts +3 -0
- package/lib/typescript/src/config/squat.d.ts.map +1 -0
- package/lib/typescript/src/config/tree-pose.d.ts +3 -0
- package/lib/typescript/src/config/tree-pose.d.ts.map +1 -0
- package/lib/typescript/src/config/tricep-dip.d.ts +3 -0
- package/lib/typescript/src/config/tricep-dip.d.ts.map +1 -0
- package/lib/typescript/src/config/wall-sit.d.ts +3 -0
- package/lib/typescript/src/config/wall-sit.d.ts.map +1 -0
- package/lib/typescript/src/config/warrior-i.d.ts +3 -0
- package/lib/typescript/src/config/warrior-i.d.ts.map +1 -0
- package/lib/typescript/src/config/warrior-ii.d.ts +3 -0
- package/lib/typescript/src/config/warrior-ii.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +14 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/nitro.json +8 -1
- package/package.json +10 -10
- package/src/config/bicep-curl.ts +64 -0
- package/src/config/chair-pose.ts +67 -0
- package/src/config/cobra-pose.ts +59 -0
- package/src/config/downward-dog.ts +68 -0
- package/src/config/lunge.ts +65 -0
- package/src/config/plank.ts +59 -0
- package/src/config/shoulder-press.ts +63 -0
- package/src/config/situp.ts +51 -0
- package/src/config/squat.ts +71 -0
- package/src/config/tree-pose.ts +59 -0
- package/src/config/tricep-dip.ts +58 -0
- package/src/config/wall-sit.ts +59 -0
- package/src/config/warrior-i.ts +82 -0
- package/src/config/warrior-ii.ts +88 -0
- package/src/index.tsx +19 -0
|
@@ -1,15 +1,43 @@
|
|
|
1
|
+
// ios/HybridPoseExercise.swift
|
|
2
|
+
|
|
1
3
|
import Foundation
|
|
2
4
|
import NitroModules
|
|
3
|
-
import MediaPipeTasksVision
|
|
4
5
|
import VisionCamera
|
|
5
6
|
import AVFoundation
|
|
7
|
+
import Vision
|
|
6
8
|
|
|
7
|
-
class
|
|
9
|
+
class HybridPoseExercise: HybridNitroPoseExercisesSpec {
|
|
8
10
|
|
|
9
|
-
// ───
|
|
10
|
-
private var poseLandmarker: PoseLandmarker?
|
|
11
|
+
// ─── Vision Framework ───────────────────────────────────────
|
|
11
12
|
private var isInitialized = false
|
|
12
13
|
|
|
14
|
+
// ─── Landmark Index Mapping ─────────────────────────────────
|
|
15
|
+
// Maps Apple Vision joint names to MediaPipe landmark indices
|
|
16
|
+
// that our JS configs expect
|
|
17
|
+
private static let visionToMediaPipeMap: [(VNHumanBodyPoseObservation.JointName, Int)] = [
|
|
18
|
+
(.nose, 0),
|
|
19
|
+
(.leftShoulder, 11),
|
|
20
|
+
(.rightShoulder, 12),
|
|
21
|
+
(.leftElbow, 13),
|
|
22
|
+
(.rightElbow, 14),
|
|
23
|
+
(.leftWrist, 15),
|
|
24
|
+
(.rightWrist, 16),
|
|
25
|
+
(.leftHip, 23),
|
|
26
|
+
(.rightHip, 24),
|
|
27
|
+
(.leftKnee, 25),
|
|
28
|
+
(.rightKnee, 26),
|
|
29
|
+
(.leftAnkle, 27),
|
|
30
|
+
(.rightAnkle, 28),
|
|
31
|
+
// Vision also provides these but they map to non-standard indices
|
|
32
|
+
// We include them for skeleton drawing
|
|
33
|
+
(.neck, 10), // approximate — MediaPipe doesn't have neck
|
|
34
|
+
(.root, 33), // hip center — not in MediaPipe, we skip
|
|
35
|
+
(.leftEar, 7),
|
|
36
|
+
(.rightEar, 8),
|
|
37
|
+
(.leftEye, 2),
|
|
38
|
+
(.rightEye, 5),
|
|
39
|
+
]
|
|
40
|
+
|
|
13
41
|
// ─── Exercise Config ────────────────────────────────────────
|
|
14
42
|
private var exerciseConfig: ExerciseConfig?
|
|
15
43
|
|
|
@@ -34,9 +62,6 @@ class NitroPoseExercises: HybridNitroPoseExercisesSpec {
|
|
|
34
62
|
private var countdownSeconds: Double = 0
|
|
35
63
|
private var countdownTimer: Timer?
|
|
36
64
|
|
|
37
|
-
private var frameCount: Int = 0
|
|
38
|
-
private let processEveryNFrames: Int = 3 // Only process every 3rd frame
|
|
39
|
-
|
|
40
65
|
// ─── Form Tracking ──────────────────────────────────────────
|
|
41
66
|
private var lastFormFeedbackTime: [String: Date] = [:]
|
|
42
67
|
private var sessionFormViolations: [FormFeedback] = []
|
|
@@ -48,6 +73,10 @@ class NitroPoseExercises: HybridNitroPoseExercisesSpec {
|
|
|
48
73
|
// ─── Pose Tracking ──────────────────────────────────────────
|
|
49
74
|
private var poseWasLost = false
|
|
50
75
|
|
|
76
|
+
// ─── Frame Throttle ─────────────────────────────────────────
|
|
77
|
+
private var frameCount: Int = 0
|
|
78
|
+
private let processEveryNFrames: Int = 3
|
|
79
|
+
|
|
51
80
|
// ─── Callbacks ──────────────────────────────────────────────
|
|
52
81
|
var onRepComplete: ((_ data: RepData) -> Void)?
|
|
53
82
|
var onPhaseChange: ((_ phase: ExercisePhase) -> Void)?
|
|
@@ -65,28 +94,15 @@ class NitroPoseExercises: HybridNitroPoseExercisesSpec {
|
|
|
65
94
|
// ═══════════════════════════════════════════════════════════
|
|
66
95
|
|
|
67
96
|
func initialize(modelPath: String) throws -> Promise<Void> {
|
|
68
|
-
|
|
69
|
-
|
|
97
|
+
// No model loading needed — Vision framework is built into iOS
|
|
70
98
|
return Promise.async { [weak self] in
|
|
71
99
|
guard let self = self else { return }
|
|
72
|
-
|
|
73
|
-
let options = PoseLandmarkerOptions()
|
|
74
|
-
options.baseOptions.modelAssetPath = modelPath
|
|
75
|
-
options.runningMode = .image
|
|
76
|
-
options.numPoses = 1
|
|
77
|
-
options.minPoseDetectionConfidence = 0.5
|
|
78
|
-
options.minPosePresenceConfidence = 0.5
|
|
79
|
-
options.minTrackingConfidence = 0.5
|
|
80
|
-
|
|
81
|
-
self.poseLandmarker = try PoseLandmarker(options: options)
|
|
82
100
|
self.isInitialized = true
|
|
83
|
-
|
|
84
|
-
print("[PoseExercise] MediaPipe initialized successfully")
|
|
101
|
+
print("[PoseExercise] Initialized with Apple Vision (no model file needed)")
|
|
85
102
|
}
|
|
86
103
|
}
|
|
87
104
|
|
|
88
105
|
func release() throws {
|
|
89
|
-
poseLandmarker = nil
|
|
90
106
|
isInitialized = false
|
|
91
107
|
_status = .idle
|
|
92
108
|
resetSession()
|
|
@@ -106,8 +122,6 @@ class NitroPoseExercises: HybridNitroPoseExercisesSpec {
|
|
|
106
122
|
// ═══════════════════════════════════════════════════════════
|
|
107
123
|
|
|
108
124
|
func startSession(targetReps: Double, countdownSeconds: Double) throws {
|
|
109
|
-
print("[PoseExercise] startSession called - target: \(targetReps), countdown: \(countdownSeconds)")
|
|
110
|
-
|
|
111
125
|
resetSession()
|
|
112
126
|
self.targetReps = targetReps
|
|
113
127
|
self.countdownSeconds = countdownSeconds
|
|
@@ -138,75 +152,90 @@ class NitroPoseExercises: HybridNitroPoseExercisesSpec {
|
|
|
138
152
|
}
|
|
139
153
|
|
|
140
154
|
// ═══════════════════════════════════════════════════════════
|
|
141
|
-
// MARK: - Frame Processing
|
|
155
|
+
// MARK: - Frame Processing (Apple Vision)
|
|
142
156
|
// ═══════════════════════════════════════════════════════════
|
|
143
157
|
|
|
144
|
-
func processFrame(frame: any HybridFrameSpec) throws {
|
|
145
|
-
|
|
158
|
+
func processFrame(frame: any HybridFrameSpec) throws {
|
|
159
|
+
guard _status == .active || _status == .countdown else { return }
|
|
160
|
+
guard isInitialized else { return }
|
|
146
161
|
|
|
147
|
-
|
|
148
|
-
|
|
162
|
+
// Frame throttle
|
|
163
|
+
frameCount += 1
|
|
164
|
+
if frameCount % processEveryNFrames != 0 { return }
|
|
149
165
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
guard isInitialized, let landmarker = poseLandmarker else {
|
|
155
|
-
print("[PoseExercise] Skipping - not initialized: \(isInitialized)")
|
|
156
|
-
return
|
|
157
|
-
}
|
|
166
|
+
// Get CMSampleBuffer from VisionCamera frame
|
|
167
|
+
guard let nativeFrame = frame as? any NativeFrame,
|
|
168
|
+
let sampleBuffer = nativeFrame.sampleBuffer else { return }
|
|
158
169
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return
|
|
162
|
-
}
|
|
163
|
-
guard let sampleBuffer = nativeFrame.sampleBuffer else {
|
|
164
|
-
print("[PoseExercise] sampleBuffer is nil")
|
|
165
|
-
return
|
|
166
|
-
}
|
|
170
|
+
// Get pixel buffer — Vision takes CVPixelBuffer directly, no color conversion needed
|
|
171
|
+
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
|
|
167
172
|
|
|
168
|
-
|
|
173
|
+
// Create Vision request
|
|
174
|
+
let request = VNDetectHumanBodyPoseRequest()
|
|
169
175
|
|
|
170
|
-
|
|
171
|
-
let
|
|
172
|
-
let result = try landmarker.detect(image: mpImage)
|
|
176
|
+
// Run synchronously on this frame processor thread
|
|
177
|
+
let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
|
|
173
178
|
|
|
174
|
-
|
|
179
|
+
do {
|
|
180
|
+
try handler.perform([request])
|
|
181
|
+
|
|
182
|
+
guard let observation = request.results?.first else {
|
|
183
|
+
// No pose detected
|
|
184
|
+
if !poseWasLost {
|
|
185
|
+
poseWasLost = true
|
|
186
|
+
onPoseLost?()
|
|
187
|
+
}
|
|
188
|
+
_landmarks = []
|
|
189
|
+
return
|
|
190
|
+
}
|
|
175
191
|
|
|
176
|
-
|
|
192
|
+
// Pose detected
|
|
177
193
|
if poseWasLost {
|
|
178
194
|
poseWasLost = false
|
|
179
195
|
onPoseRegained?()
|
|
180
196
|
}
|
|
181
197
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
198
|
+
// Map Vision joints to MediaPipe landmark array (34 slots, indices 0-33)
|
|
199
|
+
// Fill all slots with zero-visibility first
|
|
200
|
+
var landmarkArray = [Landmark](repeating: Landmark(x: 0, y: 0, z: 0, visibility: 0), count: 34)
|
|
201
|
+
|
|
202
|
+
for (jointName, mediaPipeIndex) in HybridPoseExercise.visionToMediaPipeMap {
|
|
203
|
+
guard mediaPipeIndex < 34 else { continue }
|
|
204
|
+
|
|
205
|
+
do {
|
|
206
|
+
let point = try observation.recognizedPoint(jointName)
|
|
207
|
+
|
|
208
|
+
// Vision uses bottom-left origin (0,0 = bottom-left)
|
|
209
|
+
// MediaPipe uses top-left origin (0,0 = top-left)
|
|
210
|
+
// Flip Y axis
|
|
211
|
+
let confidence = Double(point.confidence)
|
|
212
|
+
|
|
213
|
+
landmarkArray[mediaPipeIndex] = Landmark(
|
|
214
|
+
x: Double(point.location.x),
|
|
215
|
+
y: 1.0 - Double(point.location.y), // flip Y
|
|
216
|
+
z: 0, // Vision doesn't provide Z depth
|
|
217
|
+
visibility: confidence
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
// Debug logging — uncomment to verify mapping
|
|
221
|
+
// print("[PoseExercise] \(jointName.rawValue.rawValue) → index \(mediaPipeIndex): x=\(String(format: "%.3f", point.location.x)) y=\(String(format: "%.3f", 1.0 - Double(point.location.y))) conf=\(String(format: "%.2f", confidence))")
|
|
222
|
+
|
|
223
|
+
} catch {
|
|
224
|
+
// Joint not detected — leave as zero visibility
|
|
225
|
+
continue
|
|
226
|
+
}
|
|
189
227
|
}
|
|
190
228
|
|
|
191
|
-
|
|
229
|
+
_landmarks = landmarkArray
|
|
192
230
|
|
|
193
231
|
if _status == .active {
|
|
194
232
|
processExerciseLogic()
|
|
195
233
|
}
|
|
196
234
|
|
|
197
|
-
}
|
|
198
|
-
print("[PoseExercise]
|
|
199
|
-
if !poseWasLost {
|
|
200
|
-
poseWasLost = true
|
|
201
|
-
onPoseLost?()
|
|
202
|
-
}
|
|
203
|
-
_landmarks = []
|
|
235
|
+
} catch {
|
|
236
|
+
print("[PoseExercise] Vision error: \(error.localizedDescription)")
|
|
204
237
|
}
|
|
205
|
-
|
|
206
|
-
} catch {
|
|
207
|
-
print("[PoseExercise] MediaPipe error: \(error.localizedDescription)")
|
|
208
238
|
}
|
|
209
|
-
}
|
|
210
239
|
|
|
211
240
|
// ═══════════════════════════════════════════════════════════
|
|
212
241
|
// MARK: - Exercise Logic Engine
|
|
@@ -216,7 +245,6 @@ func processFrame(frame: any HybridFrameSpec) throws {
|
|
|
216
245
|
guard let config = exerciseConfig else { return }
|
|
217
246
|
guard !_landmarks.isEmpty else { return }
|
|
218
247
|
|
|
219
|
-
// 1. Calculate all angles defined in the config
|
|
220
248
|
var currentAngles: [String: Double] = [:]
|
|
221
249
|
var angleSnapshots: [AngleSnapshot] = []
|
|
222
250
|
|
|
@@ -227,6 +255,11 @@ func processFrame(frame: any HybridFrameSpec) throws {
|
|
|
227
255
|
|
|
228
256
|
guard a < _landmarks.count, b < _landmarks.count, c < _landmarks.count else { continue }
|
|
229
257
|
|
|
258
|
+
// Only calculate if all three landmarks have reasonable confidence
|
|
259
|
+
guard _landmarks[a].visibility > 0.3,
|
|
260
|
+
_landmarks[b].visibility > 0.3,
|
|
261
|
+
_landmarks[c].visibility > 0.3 else { continue }
|
|
262
|
+
|
|
230
263
|
let angle = calculateAngle(
|
|
231
264
|
pointA: _landmarks[a],
|
|
232
265
|
vertex: _landmarks[b],
|
|
@@ -239,31 +272,25 @@ func processFrame(frame: any HybridFrameSpec) throws {
|
|
|
239
272
|
|
|
240
273
|
repAngleSnapshots = angleSnapshots
|
|
241
274
|
|
|
242
|
-
//
|
|
275
|
+
// Debug logging — uncomment to see angles
|
|
276
|
+
// for (name, angle) in currentAngles {
|
|
277
|
+
// print("[PoseExercise] Angle \(name): \(String(format: "%.1f", angle))°")
|
|
278
|
+
// }
|
|
279
|
+
|
|
243
280
|
let detectedPhase = determinePhase(from: currentAngles, config: config)
|
|
244
281
|
|
|
245
282
|
if detectedPhase != _currentPhase && detectedPhase != .unknown {
|
|
246
283
|
let previousPhase = _currentPhase
|
|
247
284
|
_currentPhase = detectedPhase
|
|
248
285
|
onPhaseChange?(detectedPhase)
|
|
249
|
-
|
|
250
|
-
// 3. Update phase history for rep counting
|
|
251
286
|
handlePhaseTransition(from: previousPhase, to: detectedPhase, config: config)
|
|
252
287
|
}
|
|
253
288
|
|
|
254
|
-
// 4. Check form rules
|
|
255
289
|
checkFormRules(currentAngles: currentAngles, config: config)
|
|
256
290
|
|
|
257
|
-
// 5. Handle hold-based exercises
|
|
258
291
|
if config.type == .hold {
|
|
259
292
|
handleHoldProgress(currentAngles: currentAngles, config: config)
|
|
260
293
|
}
|
|
261
|
-
|
|
262
|
-
// Temporary debug logging — remove after testing
|
|
263
|
-
for (name, angle) in currentAngles {
|
|
264
|
-
print("[PoseExercise] Angle \(name): \(String(format: "%.1f", angle))°")
|
|
265
|
-
}
|
|
266
|
-
print("[PoseExercise] Detected phase: \(detectedPhase), Current phase: \(_currentPhase)")
|
|
267
294
|
}
|
|
268
295
|
|
|
269
296
|
// ═══════════════════════════════════════════════════════════
|
|
@@ -284,9 +311,7 @@ print("[PoseExercise] Detected phase: \(detectedPhase), Current phase: \(_curren
|
|
|
284
311
|
|
|
285
312
|
let cosAngle = max(-1.0, min(1.0, dot / (magA * magC)))
|
|
286
313
|
let angleRad = acos(cosAngle)
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
return angleDeg
|
|
314
|
+
return angleRad * (180.0 / .pi)
|
|
290
315
|
}
|
|
291
316
|
|
|
292
317
|
// ═══════════════════════════════════════════════════════════
|
|
@@ -296,7 +321,6 @@ print("[PoseExercise] Detected phase: \(detectedPhase), Current phase: \(_curren
|
|
|
296
321
|
private func determinePhase(from angles: [String: Double], config: ExerciseConfig) -> ExercisePhase {
|
|
297
322
|
for phaseThreshold in config.phases {
|
|
298
323
|
guard let angle = angles[phaseThreshold.angleName] else { continue }
|
|
299
|
-
|
|
300
324
|
if angle >= phaseThreshold.minAngle && angle <= phaseThreshold.maxAngle {
|
|
301
325
|
return phaseThreshold.phase
|
|
302
326
|
}
|
|
@@ -308,7 +332,7 @@ print("[PoseExercise] Detected phase: \(detectedPhase), Current phase: \(_curren
|
|
|
308
332
|
// MARK: - Rep Counting State Machine
|
|
309
333
|
// ═══════════════════════════════════════════════════════════
|
|
310
334
|
|
|
311
|
-
private func handlePhaseTransition(from previousPhase: ExercisePhase, to newPhase: ExercisePhase, config: ExerciseConfig) {
|
|
335
|
+
private func handlePhaseTransition(from previousPhase: ExercisePhase, to newPhase: ExercisePhase, config: ExerciseConfig) {
|
|
312
336
|
guard config.type == .rep else { return }
|
|
313
337
|
|
|
314
338
|
phaseHistory.append(newPhase)
|
|
@@ -523,5 +547,6 @@ private func handlePhaseTransition(from previousPhase: ExercisePhase, to newPhas
|
|
|
523
547
|
countdownSeconds = 0
|
|
524
548
|
countdownTimer?.invalidate()
|
|
525
549
|
countdownTimer = nil
|
|
550
|
+
frameCount = 0
|
|
526
551
|
}
|
|
527
|
-
}
|
|
552
|
+
}
|
|
@@ -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":[]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// MediaPipe Pose Landmark indices
|
|
4
|
+
// 23 = left hip, 25 = left knee, 27 = left ankle
|
|
5
|
+
// 24 = right hip, 26 = right knee, 28 = right ankle
|
|
6
|
+
// 11 = left shoulder, 12 = right shoulder
|
|
7
|
+
|
|
8
|
+
export const LUNGE_CONFIG = {
|
|
9
|
+
name: 'Lunge',
|
|
10
|
+
type: 'rep',
|
|
11
|
+
angles: [{
|
|
12
|
+
name: 'frontKnee',
|
|
13
|
+
landmarkA: 23,
|
|
14
|
+
// left hip
|
|
15
|
+
landmarkB: 25,
|
|
16
|
+
// left knee (vertex)
|
|
17
|
+
landmarkC: 27 // left ankle
|
|
18
|
+
}, {
|
|
19
|
+
name: 'backKnee',
|
|
20
|
+
landmarkA: 24,
|
|
21
|
+
// right hip
|
|
22
|
+
landmarkB: 26,
|
|
23
|
+
// right knee (vertex)
|
|
24
|
+
landmarkC: 28 // right ankle
|
|
25
|
+
}, {
|
|
26
|
+
name: 'torsoAngle',
|
|
27
|
+
landmarkA: 11,
|
|
28
|
+
// left shoulder
|
|
29
|
+
landmarkB: 23,
|
|
30
|
+
// left hip (vertex)
|
|
31
|
+
landmarkC: 25 // left knee
|
|
32
|
+
}],
|
|
33
|
+
phases: [{
|
|
34
|
+
phase: 'up',
|
|
35
|
+
angleName: 'frontKnee',
|
|
36
|
+
minAngle: 155,
|
|
37
|
+
maxAngle: 180
|
|
38
|
+
}, {
|
|
39
|
+
phase: 'down',
|
|
40
|
+
angleName: 'frontKnee',
|
|
41
|
+
minAngle: 70,
|
|
42
|
+
maxAngle: 110
|
|
43
|
+
}],
|
|
44
|
+
repSequence: ['up', 'down', 'up'],
|
|
45
|
+
formRules: [{
|
|
46
|
+
name: 'kneeOverToe',
|
|
47
|
+
message: "Don't let your front knee go past your toes",
|
|
48
|
+
severity: 'warning',
|
|
49
|
+
angleName: 'frontKnee',
|
|
50
|
+
minAngle: 75,
|
|
51
|
+
maxAngle: 180
|
|
52
|
+
}, {
|
|
53
|
+
name: 'torsoLean',
|
|
54
|
+
message: 'Keep your torso upright',
|
|
55
|
+
severity: 'warning',
|
|
56
|
+
angleName: 'torsoAngle',
|
|
57
|
+
minAngle: 75,
|
|
58
|
+
maxAngle: 180
|
|
59
|
+
}],
|
|
60
|
+
holdDurationMs: 0
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=lunge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["LUNGE_CONFIG","name","type","angles","landmarkA","landmarkB","landmarkC","phases","phase","angleName","minAngle","maxAngle","repSequence","formRules","message","severity","holdDurationMs"],"sourceRoot":"../../../src","sources":["config/lunge.ts"],"mappings":";;AAEA;AACA;AACA;AACA;;AAEA,OAAO,MAAMA,YAA4B,GAAG;EAC1CC,IAAI,EAAE,OAAO;EACbC,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,UAAU;IAChBG,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,CACF;EACDC,MAAM,EAAE,CACN;IACEC,KAAK,EAAE,IAAI;IACXC,SAAS,EAAE,WAAW;IACtBC,QAAQ,EAAE,GAAG;IACbC,QAAQ,EAAE;EACZ,CAAC,EACD;IACEH,KAAK,EAAE,MAAM;IACbC,SAAS,EAAE,WAAW;IACtBC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE;EACZ,CAAC,CACF;EACDC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC;EACjCC,SAAS,EAAE,CACT;IACEZ,IAAI,EAAE,aAAa;IACnBa,OAAO,EAAE,6CAA6C;IACtDC,QAAQ,EAAE,SAAS;IACnBN,SAAS,EAAE,WAAW;IACtBC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE;EACZ,CAAC,EACD;IACEV,IAAI,EAAE,WAAW;IACjBa,OAAO,EAAE,yBAAyB;IAClCC,QAAQ,EAAE,SAAS;IACnBN,SAAS,EAAE,YAAY;IACvBC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE;EACZ,CAAC,CACF;EACDK,cAAc,EAAE;AAClB,CAAC","ignoreList":[]}
|