Skip to content

Commit 3c06bb6

Browse files
committed
improve boundingBox
1 parent 1f13871 commit 3c06bb6

File tree

1 file changed

+46
-35
lines changed

1 file changed

+46
-35
lines changed

FitCount/Workout/QuickPoseBasicView.swift

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ enum ViewState: Equatable {
1919
case startVolume
2020
case instructions
2121
case introBoundingBox
22-
case boundingBox
22+
case boundingBox(enterTime: Date)
2323
case introExercise(Exercise)
2424
case exercise(SessionData, enterTime: Date)
2525
case results(SessionData)
@@ -34,15 +34,6 @@ enum ViewState: Equatable {
3434
return nil
3535
}
3636
}
37-
38-
var features: [QuickPose.Feature]? {
39-
switch self {
40-
case .introBoundingBox, .boundingBox:
41-
return [.inside(QuickPose.RelativeCameraEdgeInsets(top: 0.1, left: 0.2, bottom: 0.01, right: 0.2))]
42-
default:
43-
return nil
44-
}
45-
}
4637
}
4738

4839
struct QuickPoseBasicView: View {
@@ -54,7 +45,6 @@ struct QuickPoseBasicView: View {
5445
@State private var feedbackText: String? = nil
5546

5647
@State private var counter = QuickPoseThresholdCounter()
57-
@State private var unchanged = QuickPoseDoubleUnchangedDetector(similarDuration: 2, leniency: 0)
5848
@State private var state: ViewState = .startVolume
5949

6050
@State private var boundingBoxVisibility = 1.0
@@ -63,6 +53,13 @@ struct QuickPoseBasicView: View {
6353

6454
static let synthesizer = AVSpeechSynthesizer()
6555

56+
func canMoveFromBoundingBox(landmarks: QuickPose.Landmarks) -> Bool {
57+
let xsInBox = landmarks.allLandmarksForBody().allSatisfy { 0.5 - (0.8/2) < $0.x && $0.x < 0.5 + (0.8/2) }
58+
let ysInBox = landmarks.allLandmarksForBody().allSatisfy { 0.5 - (0.9/2) < $0.y && $0.y < 0.5 + (0.9/2) }
59+
60+
return xsInBox && ysInBox
61+
}
62+
6663
var body: some View {
6764
GeometryReader { geometry in
6865
VStack {
@@ -99,6 +96,29 @@ struct QuickPoseBasicView: View {
9996
.cornerRadius(8)
10097
}
10198
}
99+
case .introBoundingBox:
100+
ZStack {
101+
RoundedRectangle(cornerRadius: 15)
102+
.stroke(.red, lineWidth: 5)
103+
}
104+
.frame(width: geometry.size.width * 0.8, height: geometry.size.height * 0.9)
105+
.padding(.horizontal, (geometry.size.width * 1 - 0.8)/2)
106+
107+
case .boundingBox:
108+
ZStack {
109+
RoundedRectangle(cornerRadius: 15)
110+
.stroke(.green, lineWidth: 5)
111+
112+
RoundedRectangle(cornerRadius: 15)
113+
.fill(.green.opacity(0.5))
114+
.mask(alignment: .leading) {
115+
Rectangle()
116+
.frame(width: geometry.size.width * 0.9 * boundingBoxMaskWidth)
117+
}
118+
}
119+
.frame(width: geometry.size.width * 0.8, height: geometry.size.height * 0.9)
120+
.padding(.horizontal, (geometry.size.width * 1 - 0.8)/2)
121+
102122

103123
case .results(let results):
104124
WorkoutResultsView(sessionData: results)
@@ -115,6 +135,7 @@ struct QuickPoseBasicView: View {
115135
viewModel.popToRoot()
116136
} else {
117137
state = .results(SessionData(count: counter.state.count, seconds: 0))
138+
quickPose.stop()
118139
}
119140
}) {
120141
Image(systemName: "xmark.circle.fill")
@@ -157,49 +178,39 @@ struct QuickPoseBasicView: View {
157178
appendToJson(sessionData: sessionDataDump)
158179
}
159180

160-
quickPose.update(features: state.features ?? sessionConfig.exercise.features)
181+
quickPose.update(features: sessionConfig.exercise.features)
161182
}
162183
.onAppear() {
163184
UIApplication.shared.isIdleTimerDisabled = true
164-
quickPose.start(features: state.features ?? sessionConfig.exercise.features, onFrame: { status, image, features, feedback, landmarks in
185+
quickPose.start(features: sessionConfig.exercise.features, onFrame: { status, image, features, feedback, landmarks in
165186
overlayImage = image
166187
if case .success(_,_) = status {
167188

168189
switch state {
169190
case .introBoundingBox:
170191

171-
if let result = features.first?.value, result.value == 1.0 {
172-
unchanged.reset()
173-
state = .boundingBox
192+
if let landmarks = landmarks, canMoveFromBoundingBox(landmarks: landmarks) {
193+
state = .boundingBox(enterTime: Date())
194+
boundingBoxMaskWidth = 0
174195
}
175-
case .boundingBox:
176-
if let dictionaryEntry = features.first {
177-
let result = dictionaryEntry.value
178-
boundingBoxVisibility = result.value
179-
unchanged.count(result: result.value) {
180-
if result.value == 1 { // been in for 2 seconds
181-
boundingBoxMaskWidth = 0
182-
withAnimation(.easeInOut(duration: 1)) {
183-
boundingBoxMaskWidth = 1.0
184-
}
185-
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
186-
state = .introExercise(sessionConfig.exercise)
187-
Text2Speech(text: state.speechPrompt!).say()
188-
}
189-
} else {
190-
state = .introBoundingBox
191-
}
196+
case .boundingBox(let enterDate):
197+
if let landmarks = landmarks, canMoveFromBoundingBox(landmarks: landmarks) {
198+
let timeSinceInsideBBox = -enterDate.timeIntervalSinceNow
199+
boundingBoxMaskWidth = timeSinceInsideBBox / 2
200+
if timeSinceInsideBBox > 2 {
201+
state = .introExercise(sessionConfig.exercise)
192202
}
203+
} else {
204+
state = .introBoundingBox
193205
}
194-
195206
case .introExercise(_):
196207
DispatchQueue.main.asyncAfter(deadline: .now()+0.5) {
197208
state = .exercise(SessionData(count: 0, seconds: 0), enterTime: Date())
198209
}
199210
case .exercise(_, let enterDate):
200211
let secondsElapsed = Int(-enterDate.timeIntervalSinceNow)
201212

202-
if let feedback = feedback[.fitness(.bicepCurls)] {
213+
if let feedback = feedback[sessionConfig.exercise.features.first!] {
203214
feedbackText = feedback.displayString
204215
} else {
205216
feedbackText = nil

0 commit comments

Comments
 (0)