react-native-nitro-pose-exercises 1.1.13 → 1.1.14
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/android/build.gradle +1 -1
- package/android/gradle.properties +1 -1
- package/android/src/main/java/com/margelo/nitro/nitroposeexercises/NitroPoseExercises.kt +94 -79
- package/ios/NitroPoseExercises.swift +28 -9
- package/lib/module/NitroPoseExercises.nitro.js.map +1 -1
- package/lib/module/config/pushup.js +24 -43
- package/lib/module/config/pushup.js.map +1 -1
- package/lib/typescript/src/NitroPoseExercises.nitro.d.ts +2 -1
- package/lib/typescript/src/NitroPoseExercises.nitro.d.ts.map +1 -1
- package/lib/typescript/src/config/pushup.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JHybridNitroPoseExercisesSpec.cpp +8 -2
- package/nitrogen/generated/android/c++/JHybridNitroPoseExercisesSpec.hpp +2 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroposeexercises/HybridNitroPoseExercisesSpec.kt +6 -1
- package/nitrogen/generated/ios/NitroPoseExercises-Swift-Cxx-Umbrella.hpp +1 -0
- package/nitrogen/generated/ios/c++/HybridNitroPoseExercisesSpecSwift.hpp +12 -2
- package/nitrogen/generated/ios/swift/HybridNitroPoseExercisesSpec.swift +2 -1
- package/nitrogen/generated/ios/swift/HybridNitroPoseExercisesSpec_cxx.swift +13 -2
- package/nitrogen/generated/shared/c++/HybridNitroPoseExercisesSpec.cpp +2 -1
- package/nitrogen/generated/shared/c++/HybridNitroPoseExercisesSpec.hpp +3 -1
- package/package.json +24 -4
- package/src/NitroPoseExercises.nitro.ts +10 -2
- package/src/config/pushup.ts +19 -43
- package/android/src/main/cpp/frame_helper.cpp +0 -37
- package/android/src/main/java/com/margelo/nitro/nitroposeexercises/FrameHelper.kt +0 -12
package/android/build.gradle
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
package com.margelo.nitro.nitroposeexercises
|
|
2
2
|
|
|
3
|
-
import android.graphics.Bitmap
|
|
4
3
|
import android.graphics.Matrix
|
|
5
|
-
import android.media.Image
|
|
6
4
|
import androidx.annotation.Keep
|
|
7
5
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
8
|
-
import com.google.mlkit.vision.common.InputImage
|
|
9
6
|
import com.google.mlkit.vision.pose.PoseDetection
|
|
10
7
|
import com.google.mlkit.vision.pose.PoseDetector
|
|
11
8
|
import com.google.mlkit.vision.pose.PoseLandmark
|
|
12
9
|
import com.google.mlkit.vision.pose.defaults.PoseDetectorOptions
|
|
13
10
|
import com.margelo.nitro.NitroModules
|
|
14
11
|
import com.margelo.nitro.core.Promise
|
|
15
|
-
import com.margelo.nitro.camera.HybridFrameSpec
|
|
16
12
|
import kotlin.math.acos
|
|
17
13
|
import kotlin.math.max
|
|
18
14
|
import kotlin.math.min
|
|
19
15
|
import kotlin.math.sqrt
|
|
20
16
|
|
|
17
|
+
import android.media.Image
|
|
18
|
+
import android.graphics.Bitmap
|
|
19
|
+
import com.google.mlkit.vision.common.InputImage
|
|
20
|
+
import com.margelo.nitro.core.ArrayBuffer
|
|
21
|
+
import java.nio.ByteBuffer
|
|
22
|
+
|
|
23
|
+
import java.nio.ByteOrder
|
|
24
|
+
|
|
25
|
+
import com.margelo.nitro.camera.HybridFrameSpec
|
|
26
|
+
|
|
21
27
|
@Keep
|
|
22
28
|
@DoNotStrip
|
|
23
29
|
class NitroPoseExercises : HybridNitroPoseExercisesSpec() {
|
|
@@ -205,88 +211,97 @@ override fun isReady(): Boolean {
|
|
|
205
211
|
// Frame Processing (ML Kit — async with cached results)
|
|
206
212
|
// ═══════════════════════════════════════════════════════════
|
|
207
213
|
|
|
208
|
-
override fun processFrame(frame: HybridFrameSpec) {
|
|
209
|
-
if (_status != SessionStatus.ACTIVE && _status != SessionStatus.COUNTDOWN) return
|
|
210
|
-
if (!isInitialized || poseDetector == null) return
|
|
211
|
-
|
|
212
|
-
frameCount++
|
|
213
|
-
if (frameCount % processEveryNFrames != 0) return
|
|
214
|
-
|
|
215
|
-
try {
|
|
216
|
-
val nativeBuffer = frame.getNativeBuffer()
|
|
217
|
-
val bitmap = FrameHelper.hardwareBufferToBitmap(nativeBuffer.pointer) ?: return
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
val rotation = rotationDegreesFromFrame(frame)
|
|
221
|
-
val inputImage = InputImage.fromBitmap(bitmap, rotation)
|
|
222
|
-
|
|
223
|
-
val imageWidth = bitmap.width.toDouble()
|
|
224
|
-
val imageHeight = bitmap.height.toDouble()
|
|
225
|
-
|
|
226
|
-
poseDetector!!.process(inputImage)
|
|
227
|
-
.addOnSuccessListener { pose ->
|
|
228
|
-
val poseLandmarks = pose.allPoseLandmarks
|
|
229
|
-
|
|
230
|
-
if (poseLandmarks.isNotEmpty()) {
|
|
231
|
-
if (poseWasLost) {
|
|
232
|
-
poseWasLost = false
|
|
233
|
-
onPoseRegained?.invoke()
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
val landmarkArray = Array(34) { Landmark(x = 0.0, y = 0.0, z = 0.0, visibility = 0.0) }
|
|
237
|
-
|
|
238
|
-
for (poseLandmark in poseLandmarks) {
|
|
239
|
-
val mediaPipeIndex = mlKitToMediaPipeMap[poseLandmark.landmarkType] ?: continue
|
|
240
|
-
if (mediaPipeIndex >= 34) continue
|
|
241
|
-
|
|
242
|
-
landmarkArray[mediaPipeIndex] = Landmark(
|
|
243
|
-
x = poseLandmark.position.x.toDouble() / imageWidth,
|
|
244
|
-
y = poseLandmark.position.y.toDouble() / imageHeight,
|
|
245
|
-
z = poseLandmark.position3D.z.toDouble(),
|
|
246
|
-
visibility = poseLandmark.inFrameLikelihood.toDouble()
|
|
247
|
-
)
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
synchronized(landmarkLock) {
|
|
251
|
-
cachedLandmarks = landmarkArray
|
|
252
|
-
}
|
|
253
|
-
} else {
|
|
254
|
-
if (!poseWasLost) {
|
|
255
|
-
poseWasLost = true
|
|
256
|
-
onPoseLost?.invoke()
|
|
257
|
-
}
|
|
258
|
-
synchronized(landmarkLock) {
|
|
259
|
-
cachedLandmarks = emptyArray()
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
214
|
|
|
263
|
-
bitmap.recycle()
|
|
264
|
-
}
|
|
265
|
-
.addOnFailureListener { e ->
|
|
266
|
-
println("[PoseExercise] ML Kit error: ${e.message}")
|
|
267
|
-
bitmap.recycle()
|
|
268
|
-
}
|
|
269
215
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
currentLandmarks = cachedLandmarks.copyOf()
|
|
274
|
-
}
|
|
216
|
+
override fun processFrameAndroid(buffer: ArrayBuffer, width: Double, height: Double, rotation: Double) {
|
|
217
|
+
if (_status != SessionStatus.ACTIVE && _status != SessionStatus.COUNTDOWN) return
|
|
218
|
+
if (!isInitialized || poseDetector == null) return
|
|
275
219
|
|
|
276
|
-
|
|
220
|
+
frameCount++
|
|
221
|
+
if (frameCount % processEveryNFrames != 0) return
|
|
277
222
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
223
|
+
val w = width.toInt()
|
|
224
|
+
val h = height.toInt()
|
|
225
|
+
val rot = rotation.toInt()
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
val rgb = buffer.getBuffer(false)
|
|
229
|
+
rgb.rewind()
|
|
281
230
|
|
|
282
|
-
|
|
283
|
-
|
|
231
|
+
// RGB (3 bytes) -> ARGB_8888 (4 bytes, A=0xFF). Bitmap.Config.ARGB_8888 stores as RGBA in memory.
|
|
232
|
+
val pixelCount = w * h
|
|
233
|
+
val rgba = ByteBuffer.allocateDirect(pixelCount * 4).order(ByteOrder.nativeOrder())
|
|
234
|
+
for (i in 0 until pixelCount) {
|
|
235
|
+
val r = rgb.get()
|
|
236
|
+
val g = rgb.get()
|
|
237
|
+
val b = rgb.get()
|
|
238
|
+
rgba.put(r)
|
|
239
|
+
rgba.put(g)
|
|
240
|
+
rgba.put(b)
|
|
241
|
+
rgba.put(0xFF.toByte())
|
|
284
242
|
}
|
|
243
|
+
rgba.rewind()
|
|
244
|
+
|
|
245
|
+
val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
|
|
246
|
+
bitmap.copyPixelsFromBuffer(rgba)
|
|
247
|
+
|
|
248
|
+
val inputImage = InputImage.fromBitmap(bitmap, rot)
|
|
249
|
+
|
|
250
|
+
val rotated = rot == 90 || rot == 270
|
|
251
|
+
val imageWidth = (if (rotated) h else w).toDouble()
|
|
252
|
+
val imageHeight = (if (rotated) w else h).toDouble()
|
|
253
|
+
|
|
254
|
+
poseDetector!!.process(inputImage)
|
|
255
|
+
.addOnSuccessListener { pose ->
|
|
256
|
+
val poseLandmarks = pose.allPoseLandmarks
|
|
257
|
+
|
|
258
|
+
if (poseLandmarks.isNotEmpty()) {
|
|
259
|
+
if (poseWasLost) {
|
|
260
|
+
poseWasLost = false
|
|
261
|
+
onPoseRegained?.invoke()
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
val landmarkArray = Array(34) { Landmark(x = 0.0, y = 0.0, z = 0.0, visibility = 0.0) }
|
|
265
|
+
|
|
266
|
+
for (poseLandmark in poseLandmarks) {
|
|
267
|
+
val mediaPipeIndex = mlKitToMediaPipeMap[poseLandmark.landmarkType] ?: continue
|
|
268
|
+
if (mediaPipeIndex >= 34) continue
|
|
269
|
+
|
|
270
|
+
landmarkArray[mediaPipeIndex] = Landmark(
|
|
271
|
+
x = (poseLandmark.position3D.x / imageWidth).coerceIn(0.0, 1.0),
|
|
272
|
+
y = (poseLandmark.position3D.y / imageHeight).coerceIn(0.0, 1.0),
|
|
273
|
+
z = poseLandmark.position3D.z.toDouble(),
|
|
274
|
+
visibility = poseLandmark.inFrameLikelihood.toDouble()
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
synchronized(landmarkLock) {
|
|
279
|
+
cachedLandmarks = landmarkArray
|
|
280
|
+
_landmarks = landmarkArray
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
processExerciseLogic() // your actual method name
|
|
284
|
+
} else {
|
|
285
|
+
if (!poseWasLost) {
|
|
286
|
+
poseWasLost = true
|
|
287
|
+
onPoseLost?.invoke()
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
.addOnFailureListener { e ->
|
|
292
|
+
println("[PoseExercise] ML Kit failure: ${e.message}")
|
|
293
|
+
}
|
|
294
|
+
.addOnCompleteListener {
|
|
295
|
+
bitmap.recycle()
|
|
296
|
+
}
|
|
297
|
+
} catch (e: Exception) {
|
|
298
|
+
println("[PoseExercise] processFrameAndroid error: ${e.message}")
|
|
285
299
|
}
|
|
300
|
+
}
|
|
286
301
|
|
|
287
|
-
|
|
288
|
-
//
|
|
289
|
-
|
|
302
|
+
override fun processFrameIOS(frame: HybridFrameSpec) {
|
|
303
|
+
// no-op on Android
|
|
304
|
+
}
|
|
290
305
|
|
|
291
306
|
private fun processExerciseLogic() {
|
|
292
307
|
val config = exerciseConfig ?: return
|
|
@@ -674,4 +689,4 @@ private fun isPostureValid(family: PostureFamily, threshold: Double): Boolean {
|
|
|
674
689
|
}
|
|
675
690
|
PostureFamily.NONE -> true
|
|
676
691
|
}
|
|
677
|
-
}
|
|
692
|
+
}}
|
|
@@ -178,7 +178,7 @@ private static func cgOrientation(orientation: CameraOrientation, isMirrored: Bo
|
|
|
178
178
|
// MARK: - Frame Processing (Apple Vision)
|
|
179
179
|
// ═══════════════════════════════════════════════════════════
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
func processFrameIOS(frame: any HybridFrameSpec) throws {
|
|
182
182
|
guard _status == .active || _status == .countdown else { return }
|
|
183
183
|
|
|
184
184
|
guard isInitialized else { return }
|
|
@@ -243,8 +243,6 @@ private static func cgOrientation(orientation: CameraOrientation, isMirrored: Bo
|
|
|
243
243
|
visibility: confidence
|
|
244
244
|
)
|
|
245
245
|
|
|
246
|
-
// Debug logging — uncomment to verify mapping
|
|
247
|
-
// 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))")
|
|
248
246
|
|
|
249
247
|
} catch {
|
|
250
248
|
// Joint not detected — leave as zero visibility
|
|
@@ -263,6 +261,9 @@ private static func cgOrientation(orientation: CameraOrientation, isMirrored: Bo
|
|
|
263
261
|
}
|
|
264
262
|
}
|
|
265
263
|
|
|
264
|
+
func processFrameAndroid(buffer: ArrayBuffer, width: Double, height: Double, rotation: Double) {
|
|
265
|
+
// no-op on iOS
|
|
266
|
+
}
|
|
266
267
|
// ═══════════════════════════════════════════════════════════
|
|
267
268
|
// MARK: - Exercise Logic Engine
|
|
268
269
|
// ═══════════════════════════════════════════════════════════
|
|
@@ -369,12 +370,30 @@ private func isPostureValid(family: PostureFamily, threshold: Double) -> Bool {
|
|
|
369
370
|
let ankleY = anklesVisible ? (la.y + ra.y) / 2 : kneeY
|
|
370
371
|
|
|
371
372
|
switch family {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
373
|
+
case .horizontalprone, .supine:
|
|
374
|
+
// Case A: side view — shoulders, hips, ankles in horizontal band
|
|
375
|
+
let ys: [Double] = anklesVisible
|
|
376
|
+
? [shoulderY, hipY, ankleY]
|
|
377
|
+
: [shoulderY, hipY]
|
|
378
|
+
let ySpread = (ys.max() ?? 0) - (ys.min() ?? 0)
|
|
379
|
+
if ySpread < 0.25 {
|
|
380
|
+
return true
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Case B: front-facing — body extends away from camera along Z.
|
|
384
|
+
// Y-spread is large (head close, feet far), so check upper-body geometry.
|
|
385
|
+
let le = _landmarks[13], re = _landmarks[14]
|
|
386
|
+
let lw = _landmarks[15], rw = _landmarks[16]
|
|
387
|
+
let upperBodyVisible = le.visibility > threshold && re.visibility > threshold
|
|
388
|
+
&& lw.visibility > threshold && rw.visibility > threshold
|
|
389
|
+
guard upperBodyVisible else { return false }
|
|
390
|
+
|
|
391
|
+
let wristY = (lw.y + rw.y) / 2
|
|
392
|
+
let shoulderWidth = abs(ls.x - rs.x)
|
|
393
|
+
guard wristY > shoulderY + 0.03 else { return false }
|
|
394
|
+
guard shoulderWidth > 0.10 else { return false }
|
|
395
|
+
|
|
396
|
+
return true
|
|
378
397
|
|
|
379
398
|
case .standingupright:
|
|
380
399
|
// Torso vertical (shoulders above hips). If knees visible, check chain.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NitroModules","nitroPoseExercises","createHybridObject"],"sourceRoot":"../../src","sources":["NitroPoseExercises.nitro.ts"],"mappings":";;AAAA,SAA4BA,YAAY,QAAQ,4BAA4B;;AAI5E;;AAqBA;;AASA;;AAsCA;;AAmCA;;
|
|
1
|
+
{"version":3,"names":["NitroModules","nitroPoseExercises","createHybridObject"],"sourceRoot":"../../src","sources":["NitroPoseExercises.nitro.ts"],"mappings":";;AAAA,SAA4BA,YAAY,QAAQ,4BAA4B;;AAI5E;;AAqBA;;AASA;;AAsCA;;AAmCA;;AAoDA,MAAMC,kBAAkB,GACtBD,YAAY,CAACE,kBAAkB,CAAqB,cAAc,CAAC;AAErE,SAASD,kBAAkB","ignoreList":[]}
|
|
@@ -9,67 +9,48 @@
|
|
|
9
9
|
export const PUSHUP_CONFIG = {
|
|
10
10
|
name: 'Push-Up',
|
|
11
11
|
type: 'rep',
|
|
12
|
-
postureFamily: '
|
|
13
|
-
visibilityThreshold: 0.
|
|
14
|
-
// ← add
|
|
12
|
+
postureFamily: 'none',
|
|
13
|
+
visibilityThreshold: 0.3,
|
|
15
14
|
cameraAngle: 'front',
|
|
16
15
|
angles: [{
|
|
17
16
|
name: 'leftElbow',
|
|
18
17
|
landmarkA: 11,
|
|
19
|
-
// left shoulder
|
|
20
18
|
landmarkB: 13,
|
|
21
|
-
|
|
22
|
-
landmarkC: 15 // left wrist
|
|
19
|
+
landmarkC: 15
|
|
23
20
|
}, {
|
|
24
21
|
name: 'rightElbow',
|
|
25
22
|
landmarkA: 12,
|
|
26
|
-
// right shoulder
|
|
27
23
|
landmarkB: 14,
|
|
28
|
-
|
|
29
|
-
landmarkC: 16 // right wrist
|
|
30
|
-
}, {
|
|
31
|
-
name: 'leftHip',
|
|
32
|
-
landmarkA: 11,
|
|
33
|
-
// left shoulder
|
|
34
|
-
landmarkB: 23,
|
|
35
|
-
// left hip (vertex)
|
|
36
|
-
landmarkC: 27 // left ankle
|
|
37
|
-
}, {
|
|
38
|
-
name: 'rightHip',
|
|
39
|
-
landmarkA: 12,
|
|
40
|
-
// right shoulder
|
|
41
|
-
landmarkB: 24,
|
|
42
|
-
// right hip (vertex)
|
|
43
|
-
landmarkC: 28 // right ankle
|
|
24
|
+
landmarkC: 16
|
|
44
25
|
}],
|
|
45
|
-
phases: [
|
|
26
|
+
phases: [
|
|
27
|
+
// Calibrated for 2D-projected angles in front-facing portrait filming.
|
|
28
|
+
// Real anatomical 180° appears as ~140-150° due to perspective foreshortening.
|
|
29
|
+
{
|
|
46
30
|
phase: 'up',
|
|
47
31
|
angleName: 'leftElbow',
|
|
48
|
-
minAngle:
|
|
32
|
+
minAngle: 130,
|
|
49
33
|
maxAngle: 180
|
|
50
34
|
}, {
|
|
51
35
|
phase: 'down',
|
|
52
36
|
angleName: 'leftElbow',
|
|
53
|
-
minAngle:
|
|
54
|
-
maxAngle:
|
|
37
|
+
minAngle: 40,
|
|
38
|
+
maxAngle: 80
|
|
55
39
|
}],
|
|
56
40
|
repSequence: ['up', 'down', 'up'],
|
|
57
|
-
formRules: [
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
angleName: 'leftHip',
|
|
70
|
-
minAngle: 160,
|
|
71
|
-
maxAngle: 180
|
|
41
|
+
formRules: [
|
|
42
|
+
// Trigger when descending but stalled above true-down threshold
|
|
43
|
+
{
|
|
44
|
+
name: 'shallowRep',
|
|
45
|
+
message: 'Go lower',
|
|
46
|
+
severity: 'info',
|
|
47
|
+
angleName: 'leftElbow',
|
|
48
|
+
minAngle: 80,
|
|
49
|
+
maxAngle: 130 // "limbo zone" — too low to be up, too high to be down
|
|
50
|
+
// Note: ideally this only fires when angle has stalled, not on the way down.
|
|
51
|
+
// If your formRule engine doesn't support velocity/stall detection,
|
|
52
|
+
// accept that it'll fire briefly during transitions too.
|
|
72
53
|
}],
|
|
73
|
-
holdDurationMs: 0
|
|
54
|
+
holdDurationMs: 0
|
|
74
55
|
};
|
|
75
56
|
//# sourceMappingURL=pushup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["PUSHUP_CONFIG","name","type","postureFamily","visibilityThreshold","cameraAngle","angles","landmarkA","landmarkB","landmarkC","phases","phase","angleName","minAngle","maxAngle","repSequence","formRules","message","severity","holdDurationMs"],"sourceRoot":"../../../src","sources":["config/pushup.ts"],"mappings":";;AAEA;AACA;AACA;AACA;AACA;;AAEA,OAAO,MAAMA,aAA6B,GAAG;EAC3CC,IAAI,EAAE,SAAS;EACfC,IAAI,EAAE,KAAK;EACXC,aAAa,EAAE,
|
|
1
|
+
{"version":3,"names":["PUSHUP_CONFIG","name","type","postureFamily","visibilityThreshold","cameraAngle","angles","landmarkA","landmarkB","landmarkC","phases","phase","angleName","minAngle","maxAngle","repSequence","formRules","message","severity","holdDurationMs"],"sourceRoot":"../../../src","sources":["config/pushup.ts"],"mappings":";;AAEA;AACA;AACA;AACA;AACA;;AAEA,OAAO,MAAMA,aAA6B,GAAG;EAC3CC,IAAI,EAAE,SAAS;EACfC,IAAI,EAAE,KAAK;EACXC,aAAa,EAAE,MAAM;EACrBC,mBAAmB,EAAE,GAAG;EACxBC,WAAW,EAAE,OAAO;EACpBC,MAAM,EAAE,CACN;IAAEL,IAAI,EAAE,WAAW;IAAEM,SAAS,EAAE,EAAE;IAAEC,SAAS,EAAE,EAAE;IAAEC,SAAS,EAAE;EAAG,CAAC,EAClE;IAAER,IAAI,EAAE,YAAY;IAAEM,SAAS,EAAE,EAAE;IAAEC,SAAS,EAAE,EAAE;IAAEC,SAAS,EAAE;EAAG,CAAC,CACpE;EACDC,MAAM,EAAE;EACN;EACA;EACA;IAAEC,KAAK,EAAE,IAAI;IAAEC,SAAS,EAAE,WAAW;IAAEC,QAAQ,EAAE,GAAG;IAAEC,QAAQ,EAAE;EAAI,CAAC,EACrE;IAAEH,KAAK,EAAE,MAAM;IAAEC,SAAS,EAAE,WAAW;IAAEC,QAAQ,EAAE,EAAE;IAAEC,QAAQ,EAAE;EAAG,CAAC,CACtE;EACDC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC;EACjCC,SAAS,EAAE;EACT;EACA;IACEf,IAAI,EAAE,YAAY;IAClBgB,OAAO,EAAE,UAAU;IACnBC,QAAQ,EAAE,MAAM;IAChBN,SAAS,EAAE,WAAW;IACtBC,QAAQ,EAAE,EAAE;IACZC,QAAQ,EAAE,GAAG,CAAE;IACf;IACA;IACA;EACF,CAAC,CACF;EACDK,cAAc,EAAE;AAClB,CAAC","ignoreList":[]}
|
|
@@ -80,7 +80,8 @@ interface NitroPoseExercises extends HybridObject<{
|
|
|
80
80
|
release(): void;
|
|
81
81
|
loadExercise(config: ExerciseConfig): void;
|
|
82
82
|
readonly status: SessionStatus;
|
|
83
|
-
|
|
83
|
+
processFrameIOS(frame: Frame): void;
|
|
84
|
+
processFrameAndroid(buffer: ArrayBuffer, width: number, height: number, rotation: number): void;
|
|
84
85
|
onRepComplete: ((data: RepData) => void) | undefined;
|
|
85
86
|
onPhaseChange: ((phase: ExercisePhase) => void) | undefined;
|
|
86
87
|
onFormFeedback: ((feedback: FormFeedback) => void) | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NitroPoseExercises.nitro.d.ts","sourceRoot":"","sources":["../../../src/NitroPoseExercises.nitro.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"NitroPoseExercises.nitro.d.ts","sourceRoot":"","sources":["../../../src/NitroPoseExercises.nitro.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pushup.d.ts","sourceRoot":"","sources":["../../../../src/config/pushup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAQlE,eAAO,MAAM,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"pushup.d.ts","sourceRoot":"","sources":["../../../../src/config/pushup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAQlE,eAAO,MAAM,aAAa,EAAE,cAgC3B,CAAC"}
|
|
@@ -91,6 +91,8 @@ namespace margelo::nitro::camera { class HybridFrameSpec; }
|
|
|
91
91
|
#include <memory>
|
|
92
92
|
#include <VisionCamera/HybridFrameSpec.hpp>
|
|
93
93
|
#include <VisionCamera/JHybridFrameSpec.hpp>
|
|
94
|
+
#include <NitroModules/ArrayBuffer.hpp>
|
|
95
|
+
#include <NitroModules/JArrayBuffer.hpp>
|
|
94
96
|
|
|
95
97
|
namespace margelo::nitro::nitroposeexercises {
|
|
96
98
|
|
|
@@ -329,10 +331,14 @@ namespace margelo::nitro::nitroposeexercises {
|
|
|
329
331
|
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<JExerciseConfig> /* config */)>("loadExercise");
|
|
330
332
|
method(_javaPart, JExerciseConfig::fromCpp(config));
|
|
331
333
|
}
|
|
332
|
-
void JHybridNitroPoseExercisesSpec::
|
|
333
|
-
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<margelo::nitro::camera::JHybridFrameSpec::JavaPart> /* frame */)>("
|
|
334
|
+
void JHybridNitroPoseExercisesSpec::processFrameIOS(const std::shared_ptr<margelo::nitro::camera::HybridFrameSpec>& frame) {
|
|
335
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<margelo::nitro::camera::JHybridFrameSpec::JavaPart> /* frame */)>("processFrameIOS");
|
|
334
336
|
method(_javaPart, std::dynamic_pointer_cast<margelo::nitro::camera::JHybridFrameSpec>(frame)->getJavaPart());
|
|
335
337
|
}
|
|
338
|
+
void JHybridNitroPoseExercisesSpec::processFrameAndroid(const std::shared_ptr<ArrayBuffer>& buffer, double width, double height, double rotation) {
|
|
339
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<JArrayBuffer::javaobject> /* buffer */, double /* width */, double /* height */, double /* rotation */)>("processFrameAndroid");
|
|
340
|
+
method(_javaPart, JArrayBuffer::wrap(buffer), width, height, rotation);
|
|
341
|
+
}
|
|
336
342
|
void JHybridNitroPoseExercisesSpec::startSession(double targetReps, double countdownSeconds) {
|
|
337
343
|
static const auto method = _javaPart->javaClassStatic()->getMethod<void(double /* targetReps */, double /* countdownSeconds */)>("startSession");
|
|
338
344
|
method(_javaPart, targetReps, countdownSeconds);
|
|
@@ -78,7 +78,8 @@ namespace margelo::nitro::nitroposeexercises {
|
|
|
78
78
|
std::shared_ptr<Promise<void>> initialize(const std::string& modelPath) override;
|
|
79
79
|
void release() override;
|
|
80
80
|
void loadExercise(const ExerciseConfig& config) override;
|
|
81
|
-
void
|
|
81
|
+
void processFrameIOS(const std::shared_ptr<margelo::nitro::camera::HybridFrameSpec>& frame) override;
|
|
82
|
+
void processFrameAndroid(const std::shared_ptr<ArrayBuffer>& buffer, double width, double height, double rotation) override;
|
|
82
83
|
void startSession(double targetReps, double countdownSeconds) override;
|
|
83
84
|
void pauseSession() override;
|
|
84
85
|
void resumeSession() override;
|
|
@@ -12,6 +12,7 @@ import com.facebook.jni.HybridData
|
|
|
12
12
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
13
13
|
import com.margelo.nitro.core.Promise
|
|
14
14
|
import com.margelo.nitro.camera.HybridFrameSpec
|
|
15
|
+
import com.margelo.nitro.core.ArrayBuffer
|
|
15
16
|
import com.margelo.nitro.core.HybridObject
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -184,7 +185,11 @@ abstract class HybridNitroPoseExercisesSpec: HybridObject() {
|
|
|
184
185
|
|
|
185
186
|
@DoNotStrip
|
|
186
187
|
@Keep
|
|
187
|
-
abstract fun
|
|
188
|
+
abstract fun processFrameIOS(frame: com.margelo.nitro.camera.HybridFrameSpec): Unit
|
|
189
|
+
|
|
190
|
+
@DoNotStrip
|
|
191
|
+
@Keep
|
|
192
|
+
abstract fun processFrameAndroid(buffer: ArrayBuffer, width: Double, height: Double, rotation: Double): Unit
|
|
188
193
|
|
|
189
194
|
@DoNotStrip
|
|
190
195
|
@Keep
|
|
@@ -63,6 +63,7 @@ namespace margelo::nitro::nitroposeexercises { enum class SessionStatus; }
|
|
|
63
63
|
#include "RepData.hpp"
|
|
64
64
|
#include "SessionResult.hpp"
|
|
65
65
|
#include "SessionStatus.hpp"
|
|
66
|
+
#include <NitroModules/ArrayBuffer.hpp>
|
|
66
67
|
#include <NitroModules/Promise.hpp>
|
|
67
68
|
#include <NitroModules/Result.hpp>
|
|
68
69
|
#include <VisionCamera/HybridFrameSpec.hpp>
|
|
@@ -46,6 +46,8 @@ namespace margelo::nitro::nitroposeexercises { enum class PostureFamily; }
|
|
|
46
46
|
namespace margelo::nitro::nitroposeexercises { enum class CameraAngleType; }
|
|
47
47
|
// Forward declaration of `HybridFrameSpec` to properly resolve imports.
|
|
48
48
|
namespace margelo::nitro::camera { class HybridFrameSpec; }
|
|
49
|
+
// Forward declaration of `ArrayBufferHolder` to properly resolve imports.
|
|
50
|
+
namespace NitroModules { class ArrayBufferHolder; }
|
|
49
51
|
|
|
50
52
|
#include "SessionStatus.hpp"
|
|
51
53
|
#include "RepData.hpp"
|
|
@@ -70,6 +72,8 @@ namespace margelo::nitro::camera { class HybridFrameSpec; }
|
|
|
70
72
|
#include "CameraAngleType.hpp"
|
|
71
73
|
#include <memory>
|
|
72
74
|
#include <VisionCamera/HybridFrameSpec.hpp>
|
|
75
|
+
#include <NitroModules/ArrayBuffer.hpp>
|
|
76
|
+
#include <NitroModules/ArrayBufferHolder.hpp>
|
|
73
77
|
|
|
74
78
|
#include "NitroPoseExercises-Swift-Cxx-Umbrella.hpp"
|
|
75
79
|
|
|
@@ -218,8 +222,14 @@ namespace margelo::nitro::nitroposeexercises {
|
|
|
218
222
|
std::rethrow_exception(__result.error());
|
|
219
223
|
}
|
|
220
224
|
}
|
|
221
|
-
inline void
|
|
222
|
-
auto __result = _swiftPart.
|
|
225
|
+
inline void processFrameIOS(const std::shared_ptr<margelo::nitro::camera::HybridFrameSpec>& frame) override {
|
|
226
|
+
auto __result = _swiftPart.processFrameIOS(frame);
|
|
227
|
+
if (__result.hasError()) [[unlikely]] {
|
|
228
|
+
std::rethrow_exception(__result.error());
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
inline void processFrameAndroid(const std::shared_ptr<ArrayBuffer>& buffer, double width, double height, double rotation) override {
|
|
232
|
+
auto __result = _swiftPart.processFrameAndroid(ArrayBufferHolder(buffer), std::forward<decltype(width)>(width), std::forward<decltype(height)>(height), std::forward<decltype(rotation)>(rotation));
|
|
223
233
|
if (__result.hasError()) [[unlikely]] {
|
|
224
234
|
std::rethrow_exception(__result.error());
|
|
225
235
|
}
|
|
@@ -29,7 +29,8 @@ public protocol HybridNitroPoseExercisesSpec_protocol: HybridObject {
|
|
|
29
29
|
func initialize(modelPath: String) throws -> Promise<Void>
|
|
30
30
|
func release() throws -> Void
|
|
31
31
|
func loadExercise(config: ExerciseConfig) throws -> Void
|
|
32
|
-
func
|
|
32
|
+
func processFrameIOS(frame: (any HybridFrameSpec)) throws -> Void
|
|
33
|
+
func processFrameAndroid(buffer: ArrayBuffer, width: Double, height: Double, rotation: Double) throws -> Void
|
|
33
34
|
func startSession(targetReps: Double, countdownSeconds: Double) throws -> Void
|
|
34
35
|
func pauseSession() throws -> Void
|
|
35
36
|
func resumeSession() throws -> Void
|
|
@@ -487,9 +487,9 @@ open class HybridNitroPoseExercisesSpec_cxx {
|
|
|
487
487
|
}
|
|
488
488
|
|
|
489
489
|
@inline(__always)
|
|
490
|
-
public final func
|
|
490
|
+
public final func processFrameIOS(frame: bridge.std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_) -> bridge.Result_void_ {
|
|
491
491
|
do {
|
|
492
|
-
try self.__implementation.
|
|
492
|
+
try self.__implementation.processFrameIOS(frame: { () -> any HybridFrameSpec in
|
|
493
493
|
let __unsafePointer = bridge.get_std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_(frame)
|
|
494
494
|
let __instance = HybridFrameSpec_cxx.fromUnsafe(__unsafePointer)
|
|
495
495
|
return __instance.getHybridFrameSpec()
|
|
@@ -501,6 +501,17 @@ open class HybridNitroPoseExercisesSpec_cxx {
|
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
+
@inline(__always)
|
|
505
|
+
public final func processFrameAndroid(buffer: ArrayBuffer, width: Double, height: Double, rotation: Double) -> bridge.Result_void_ {
|
|
506
|
+
do {
|
|
507
|
+
try self.__implementation.processFrameAndroid(buffer: buffer, width: width, height: height, rotation: rotation)
|
|
508
|
+
return bridge.create_Result_void_()
|
|
509
|
+
} catch (let __error) {
|
|
510
|
+
let __exceptionPtr = __error.toCpp()
|
|
511
|
+
return bridge.create_Result_void_(__exceptionPtr)
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
504
515
|
@inline(__always)
|
|
505
516
|
public final func startSession(targetReps: Double, countdownSeconds: Double) -> bridge.Result_void_ {
|
|
506
517
|
do {
|
|
@@ -39,7 +39,8 @@ namespace margelo::nitro::nitroposeexercises {
|
|
|
39
39
|
prototype.registerHybridMethod("initialize", &HybridNitroPoseExercisesSpec::initialize);
|
|
40
40
|
prototype.registerHybridMethod("release", &HybridNitroPoseExercisesSpec::release);
|
|
41
41
|
prototype.registerHybridMethod("loadExercise", &HybridNitroPoseExercisesSpec::loadExercise);
|
|
42
|
-
prototype.registerHybridMethod("
|
|
42
|
+
prototype.registerHybridMethod("processFrameIOS", &HybridNitroPoseExercisesSpec::processFrameIOS);
|
|
43
|
+
prototype.registerHybridMethod("processFrameAndroid", &HybridNitroPoseExercisesSpec::processFrameAndroid);
|
|
43
44
|
prototype.registerHybridMethod("startSession", &HybridNitroPoseExercisesSpec::startSession);
|
|
44
45
|
prototype.registerHybridMethod("pauseSession", &HybridNitroPoseExercisesSpec::pauseSession);
|
|
45
46
|
prototype.registerHybridMethod("resumeSession", &HybridNitroPoseExercisesSpec::resumeSession);
|
|
@@ -47,6 +47,7 @@ namespace margelo::nitro::camera { class HybridFrameSpec; }
|
|
|
47
47
|
#include "ExerciseConfig.hpp"
|
|
48
48
|
#include <memory>
|
|
49
49
|
#include <VisionCamera/HybridFrameSpec.hpp>
|
|
50
|
+
#include <NitroModules/ArrayBuffer.hpp>
|
|
50
51
|
|
|
51
52
|
namespace margelo::nitro::nitroposeexercises {
|
|
52
53
|
|
|
@@ -103,7 +104,8 @@ namespace margelo::nitro::nitroposeexercises {
|
|
|
103
104
|
virtual std::shared_ptr<Promise<void>> initialize(const std::string& modelPath) = 0;
|
|
104
105
|
virtual void release() = 0;
|
|
105
106
|
virtual void loadExercise(const ExerciseConfig& config) = 0;
|
|
106
|
-
virtual void
|
|
107
|
+
virtual void processFrameIOS(const std::shared_ptr<margelo::nitro::camera::HybridFrameSpec>& frame) = 0;
|
|
108
|
+
virtual void processFrameAndroid(const std::shared_ptr<ArrayBuffer>& buffer, double width, double height, double rotation) = 0;
|
|
107
109
|
virtual void startSession(double targetReps, double countdownSeconds) = 0;
|
|
108
110
|
virtual void pauseSession() = 0;
|
|
109
111
|
virtual void resumeSession() = 0;
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-pose-exercises",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.14",
|
|
4
4
|
"description": "Real-time on-device exercise tracking for React Native. Rep counting, form validation, and skeleton overlay powered by Apple Vision (iOS) and Google ML Kit (Android) with VisionCamera v5 via Nitro Modules.",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
7
|
+
"installConfig": {
|
|
8
|
+
"hoistingLimits": "workspaces"
|
|
9
|
+
},
|
|
7
10
|
"exports": {
|
|
8
11
|
".": {
|
|
9
12
|
"source": "./src/index.tsx",
|
|
@@ -89,8 +92,10 @@
|
|
|
89
92
|
"react-native": "0.85.3",
|
|
90
93
|
"react-native-builder-bob": "^0.41.0",
|
|
91
94
|
"react-native-nitro-modules": "^0.35.9",
|
|
95
|
+
"react-native-reanimated": "4.4.0",
|
|
92
96
|
"react-native-svg": "^15.15.5",
|
|
93
97
|
"react-native-vision-camera": "^5.0.11",
|
|
98
|
+
"react-native-vision-camera-resizer": "^5.0.11",
|
|
94
99
|
"react-native-vision-camera-worklets": "^5.0.11",
|
|
95
100
|
"react-native-worklets": "^0.9.1",
|
|
96
101
|
"release-it": "^19.2.4",
|
|
@@ -103,12 +108,27 @@
|
|
|
103
108
|
"react-native-nitro-modules": "*",
|
|
104
109
|
"react-native-reanimated": "*",
|
|
105
110
|
"react-native-vision-camera": "*",
|
|
111
|
+
"react-native-vision-camera-resizer": "*",
|
|
106
112
|
"react-native-vision-camera-worklets": "*",
|
|
107
113
|
"react-native-worklets": "*"
|
|
108
114
|
},
|
|
109
|
-
"workspaces":
|
|
110
|
-
"
|
|
111
|
-
|
|
115
|
+
"workspaces": {
|
|
116
|
+
"packages": [
|
|
117
|
+
"example"
|
|
118
|
+
],
|
|
119
|
+
"nohoist": [
|
|
120
|
+
"**/react-native",
|
|
121
|
+
"**/react-native/**",
|
|
122
|
+
"**/react-native-vision-camera",
|
|
123
|
+
"**/react-native-vision-camera/**",
|
|
124
|
+
"**/react-native-vision-camera-resizer",
|
|
125
|
+
"**/react-native-vision-camera-resizer/**",
|
|
126
|
+
"**/react-native-vision-camera-worklets",
|
|
127
|
+
"**/react-native-vision-camera-worklets/**",
|
|
128
|
+
"**/react-native-nitro-modules",
|
|
129
|
+
"**/react-native-nitro-modules/**"
|
|
130
|
+
]
|
|
131
|
+
},
|
|
112
132
|
"packageManager": "yarn@4.11.0",
|
|
113
133
|
"react-native-builder-bob": {
|
|
114
134
|
"source": "src",
|
|
@@ -121,8 +121,16 @@ interface NitroPoseExercises extends HybridObject<{
|
|
|
121
121
|
// Session control
|
|
122
122
|
readonly status: SessionStatus;
|
|
123
123
|
|
|
124
|
-
//
|
|
125
|
-
|
|
124
|
+
// iOS: zero-copy Vision path
|
|
125
|
+
processFrameIOS(frame: Frame): void;
|
|
126
|
+
|
|
127
|
+
// Android: pre-resized RGBA buffer from VisionCamera Resizer (Vulkan-accelerated)
|
|
128
|
+
processFrameAndroid(
|
|
129
|
+
buffer: ArrayBuffer,
|
|
130
|
+
width: number,
|
|
131
|
+
height: number,
|
|
132
|
+
rotation: number
|
|
133
|
+
): void;
|
|
126
134
|
|
|
127
135
|
// Callbacks (set from JS side)
|
|
128
136
|
onRepComplete: ((data: RepData) => void) | undefined;
|
package/src/config/pushup.ts
CHANGED
|
@@ -9,57 +9,33 @@ import type { ExerciseConfig } from '../NitroPoseExercises.nitro';
|
|
|
9
9
|
export const PUSHUP_CONFIG: ExerciseConfig = {
|
|
10
10
|
name: 'Push-Up',
|
|
11
11
|
type: 'rep',
|
|
12
|
-
postureFamily: '
|
|
13
|
-
visibilityThreshold: 0.
|
|
12
|
+
postureFamily: 'none',
|
|
13
|
+
visibilityThreshold: 0.3,
|
|
14
14
|
cameraAngle: 'front',
|
|
15
15
|
angles: [
|
|
16
|
-
{
|
|
17
|
-
|
|
18
|
-
landmarkA: 11, // left shoulder
|
|
19
|
-
landmarkB: 13, // left elbow (vertex)
|
|
20
|
-
landmarkC: 15, // left wrist
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: 'rightElbow',
|
|
24
|
-
landmarkA: 12, // right shoulder
|
|
25
|
-
landmarkB: 14, // right elbow (vertex)
|
|
26
|
-
landmarkC: 16, // right wrist
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
name: 'leftHip',
|
|
30
|
-
landmarkA: 11, // left shoulder
|
|
31
|
-
landmarkB: 23, // left hip (vertex)
|
|
32
|
-
landmarkC: 27, // left ankle
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: 'rightHip',
|
|
36
|
-
landmarkA: 12, // right shoulder
|
|
37
|
-
landmarkB: 24, // right hip (vertex)
|
|
38
|
-
landmarkC: 28, // right ankle
|
|
39
|
-
},
|
|
16
|
+
{ name: 'leftElbow', landmarkA: 11, landmarkB: 13, landmarkC: 15 },
|
|
17
|
+
{ name: 'rightElbow', landmarkA: 12, landmarkB: 14, landmarkC: 16 },
|
|
40
18
|
],
|
|
41
19
|
phases: [
|
|
42
|
-
|
|
43
|
-
|
|
20
|
+
// Calibrated for 2D-projected angles in front-facing portrait filming.
|
|
21
|
+
// Real anatomical 180° appears as ~140-150° due to perspective foreshortening.
|
|
22
|
+
{ phase: 'up', angleName: 'leftElbow', minAngle: 130, maxAngle: 180 },
|
|
23
|
+
{ phase: 'down', angleName: 'leftElbow', minAngle: 40, maxAngle: 80 },
|
|
44
24
|
],
|
|
45
25
|
repSequence: ['up', 'down', 'up'],
|
|
46
26
|
formRules: [
|
|
27
|
+
// Trigger when descending but stalled above true-down threshold
|
|
47
28
|
{
|
|
48
|
-
name: '
|
|
49
|
-
message: '
|
|
50
|
-
severity: '
|
|
51
|
-
angleName: '
|
|
52
|
-
minAngle:
|
|
53
|
-
maxAngle:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
message: "Lower your hips — you're piking up",
|
|
58
|
-
severity: 'warning',
|
|
59
|
-
angleName: 'leftHip',
|
|
60
|
-
minAngle: 160,
|
|
61
|
-
maxAngle: 180,
|
|
29
|
+
name: 'shallowRep',
|
|
30
|
+
message: 'Go lower',
|
|
31
|
+
severity: 'info',
|
|
32
|
+
angleName: 'leftElbow',
|
|
33
|
+
minAngle: 80,
|
|
34
|
+
maxAngle: 130, // "limbo zone" — too low to be up, too high to be down
|
|
35
|
+
// Note: ideally this only fires when angle has stalled, not on the way down.
|
|
36
|
+
// If your formRule engine doesn't support velocity/stall detection,
|
|
37
|
+
// accept that it'll fire briefly during transitions too.
|
|
62
38
|
},
|
|
63
39
|
],
|
|
64
|
-
holdDurationMs: 0,
|
|
40
|
+
holdDurationMs: 0,
|
|
65
41
|
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
#include <jni.h>
|
|
2
|
-
#include <android/hardware_buffer_jni.h>
|
|
3
|
-
|
|
4
|
-
extern "C"
|
|
5
|
-
JNIEXPORT jobject JNICALL
|
|
6
|
-
Java_com_margelo_nitro_nitroposeexercises_FrameHelper_hardwareBufferToBitmap(
|
|
7
|
-
JNIEnv *env,
|
|
8
|
-
jclass clazz,
|
|
9
|
-
jlong pointer
|
|
10
|
-
) {
|
|
11
|
-
AHardwareBuffer *buffer = reinterpret_cast<AHardwareBuffer *>(pointer);
|
|
12
|
-
if (!buffer) return nullptr;
|
|
13
|
-
|
|
14
|
-
// Convert AHardwareBuffer* to Java HardwareBuffer
|
|
15
|
-
jobject hardwareBuffer = AHardwareBuffer_toHardwareBuffer(env, buffer);
|
|
16
|
-
if (!hardwareBuffer) return nullptr;
|
|
17
|
-
|
|
18
|
-
// Call Bitmap.wrapHardwareBuffer(hardwareBuffer, null) to create a hardware-backed Bitmap
|
|
19
|
-
jclass bitmapClass = env->FindClass("android/graphics/Bitmap");
|
|
20
|
-
jmethodID wrapMethod = env->GetStaticMethodID(
|
|
21
|
-
bitmapClass,
|
|
22
|
-
"wrapHardwareBuffer",
|
|
23
|
-
"(Landroid/hardware/HardwareBuffer;Landroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;"
|
|
24
|
-
);
|
|
25
|
-
jobject hwBitmap = env->CallStaticObjectMethod(bitmapClass, wrapMethod, hardwareBuffer, nullptr);
|
|
26
|
-
if (!hwBitmap) return nullptr;
|
|
27
|
-
|
|
28
|
-
// Copy to a software ARGB_8888 Bitmap (ML Kit needs software bitmap)
|
|
29
|
-
jclass configClass = env->FindClass("android/graphics/Bitmap$Config");
|
|
30
|
-
jfieldID argbField = env->GetStaticFieldID(configClass, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
|
|
31
|
-
jobject argbConfig = env->GetStaticObjectField(configClass, argbField);
|
|
32
|
-
|
|
33
|
-
jmethodID copyMethod = env->GetMethodID(bitmapClass, "copy", "(Landroid/graphics/Bitmap$Config;Z)Landroid/graphics/Bitmap;");
|
|
34
|
-
jobject softBitmap = env->CallObjectMethod(hwBitmap, copyMethod, argbConfig, JNI_FALSE);
|
|
35
|
-
|
|
36
|
-
return softBitmap;
|
|
37
|
-
}
|