Skip to content

Commit

Permalink
Merge pull request #19 from jhrcook/spinner-engine
Browse files Browse the repository at this point in the history
New spinner engine
  • Loading branch information
jhrcook authored Jan 29, 2021
2 parents f205c18 + 6816565 commit 943b262
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ enum UserDefaultsKeys: String {
}

extension UserDefaults {
static func readCrownVelocityMultiplier() -> Double {
static func readCrownVelocityMultiplier(defaultValue: Double = 3.0) -> Double {
let m = standard.double(forKey: UserDefaultsKeys.crownVelocityMultiplier.rawValue)
return m > 0.0 ? m : 1.0
return m > 0.0 ? m : defaultValue
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@

import Foundation

class CrownVelocityCalculator {
class WheelVelocityTracker: ObservableObject {
private var history = [Double]()
var currentVelocity: Double = 0.0

var velocityThreshold: Double = 500.0
var velocityThreshold: Double = 50.0
private(set) var didPassThreshold: Bool = false

var memory: Int = 10
private(set) var currentVelocity = 0.0

init() {}

Expand All @@ -40,15 +38,14 @@ class CrownVelocityCalculator {
history = history.suffix(memory)
}

var diffs: Double = 0.0
for i in 0 ..< (history.count - 1) {
diffs += history[i + 1] - history[i]
}

currentVelocity = diffs / Double(history.count - 1)
currentVelocity = average(history)
checkThreshold()
}

private func average(_ x: [Double]) -> Double {
x.reduce(0, +) / Double(x.count)
}

func checkThreshold() {
if !didPassThreshold {
didPassThreshold = abs(currentVelocity) > velocityThreshold
Expand Down
19 changes: 19 additions & 0 deletions Workout Spinner WatchKit Extension/Protocols/NumericType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// NumericType.swift
// Workout Spinner WatchKit Extension
//
// Created by Joshua on 1/28/21.
// Copyright © 2021 Joshua Cook. All rights reserved.
//

import Foundation

protocol NumericType: Comparable {
static func + (lhs: Self, rhs: Self) -> Self
static func - (lhs: Self, rhs: Self) -> Self
static func * (lhs: Self, rhs: Self) -> Self
static func / (lhs: Self, rhs: Self) -> Self
}

extension Double: NumericType {}
extension Int: NumericType {}
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,25 @@
import SwiftUI

extension ExercisePicker {
func crownRotationDidChange(crownValue _: Double) {
crownVelocity.update(newValue: wheelRotation)
func crownRotationDidChange(crownValue: Double) {
if crownValue == previousCrownRotation { return }
previousCrownRotation = crownValue
spinningWheel.crownInput(angle: crownValue * crownVelocityMultiplier, at: Date())
velocityTracker.update(newValue: spinningWheel.wheelVelocity)
readSelectedWorkout()
}

func rotationEffectDidFinish() {
if crownVelocity.didPassThreshold {
if velocityTracker.didPassThreshold {
exerciseSelected = true
crownVelocity.resetThreshold()
velocityTracker.resetThreshold()
}
}

func readSelectedWorkout() {
let pointerAngle = 180.0
let sliceAngle = 360.0 / Double(numExercises)
let pointingAtAngle = (0.5 * sliceAngle) + (pointerAngle - spinDirection * wheelRotation)
let pointingAtAngle = (0.5 * sliceAngle) + (pointerAngle - spinDirection * spinningWheel.wheelRotation)
.truncatingRemainder(dividingBy: 360.0)
var pointingSlice = (pointingAtAngle / sliceAngle).rounded(.down)

Expand Down
31 changes: 19 additions & 12 deletions Workout Spinner WatchKit Extension/Views/ExercisePicker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,35 @@ import Combine
import os
import SwiftUI

import CrownRotationToSpinningWheel

struct ExercisePicker: View {
// Global objects.
@ObservedObject var workoutManager: WorkoutManager
@ObservedObject var exerciseOptions: ExerciseOptions
@State internal var crownRotation = 0.0
var wheelRotation: Double {
crownRotation * crownVelocityMultiplier
}

var numExercises: Int {
return exerciseOptions.exercisesBlacklistFiltered.count
exerciseOptions.exercisesBlacklistFiltered.count
}

var crownVelocity = CrownVelocityCalculator(velocityThreshold: 50, memory: 20)
var crownVelocityMultiplier = UserDefaults.readCrownVelocityMultiplier()

@Binding internal var exerciseSelected: Bool
@State internal var selectedExerciseIndex: Int = 0
// Spinning wheel variables.
@StateObject var spinningWheel = SpinningWheel(damping: 0.07, crownVelocityMemory: 1.0)
@StateObject var velocityTracker = WheelVelocityTracker(velocityThreshold: 5, memory: 3)
@State internal var previousCrownRotation = 0.0
@State internal var crownRotation = 0.0

// Spinning wheel constants.
var spinDirection: Double {
return WKInterfaceDevice().crownOrientation == .left ? 1.0 : -1.0
}

internal var crownVelocityMultiplier = UserDefaults.readCrownVelocityMultiplier()

// Exercise selection variables.
@Binding internal var exerciseSelected: Bool
@State internal var selectedExerciseIndex: Int = 0

// Logging
let logger = Logger.exercisePickerLogger

init(workoutManager: WorkoutManager, exerciseOptions: ExerciseOptions, exerciseSelected: Binding<Bool>) {
Expand Down Expand Up @@ -62,10 +69,10 @@ struct ExercisePicker: View {
}
}
.modifier(SpinnerRotationModifier(
rotation: .degrees(self.spinDirection * self.wheelRotation),
rotation: .degrees(self.spinDirection * self.spinningWheel.wheelRotation),
onFinishedRotationAnimation: self.rotationEffectDidFinish
))
.animation(.default)
.animation(.spring())

HStack {
SpinnerPointer()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// PlusMinusStepper.swift
// Workout Spinner WatchKit Extension
//
// Created by Joshua on 1/28/21.
// Copyright © 2021 Joshua Cook. All rights reserved.
//

import SwiftUI

struct PlusMinusStepper<T: NumericType>: View {
@Binding var value: T
let step: T
var min: T? = nil
var max: T? = nil
var label: Text
var extraDownAction: (() -> Void)? = nil
var extraUpAction: (() -> Void)? = nil

var body: some View {
HStack {
Button(action: {
value = value - step
if let f = extraDownAction { f() }
}, label: {
Image(systemName: "minus.circle").foregroundColor(.red).font(.system(size: 25))
})
.disabled(min != nil ? (value - step) < min! : false)
.buttonStyle(PlainButtonStyle())
.padding(2)
.frame(width: 60)

Spacer()

label.padding(3)

Spacer()

Button(action: {
value = value + step
if let f = extraUpAction { f() }
}, label: {
Image(systemName: "plus.circle").foregroundColor(.green).font(.system(size: 25))
})
.disabled(max != nil ? (value + step) > max! : false)
.buttonStyle(PlainButtonStyle())
.padding(2)
.frame(width: 60)
}
}
}

#if DEBUG
struct PlusMinusStepper_Previews: PreviewProvider {
static var previews: some View {
PlusMinusStepper<Int>(value: .constant(3), step: 1, label: Text("3")).previewLayout(.fixed(width: 100, height: 50))
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,7 @@ struct Settings: View {

@State private var crownVelocityMultiplier = UserDefaults.readCrownVelocityMultiplier()

private var crownVelocityMultiplierValues: [Double] = {
var a: [Double] = []
for x in 1 ... 10 {
a.append(Double(x) * 0.5)
}
return a
}()
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String

@Environment(\.presentationMode) var presentationMode
let logger = Logger.settingsLogger
Expand Down Expand Up @@ -75,12 +69,7 @@ struct Settings: View {
}

Section(header: SpinningImageSectionHeader(imageName: "hexagon", text: "Spinning Wheel")) {
Picker(selection: $crownVelocityMultiplier, label: Text("Spin speed"), content: {
ForEach(crownVelocityMultiplierValues, id: \.self) { x in
Text("\(x, specifier: "%.1f")")
}
})
.pickerStyle(WheelPickerStyle())
PlusMinusStepper(value: $crownVelocityMultiplier, step: 1, min: 1, max: 10, label: Text("\(Int(crownVelocityMultiplier))"))
}

Section(header: SectionHeader(imageName: "info.circle", text: "About")) {
Expand All @@ -92,7 +81,7 @@ struct Settings: View {
HStack {
Text("Version")
Spacer()
Text("1.2.2")
Text(appVersion ?? "1.3.2")
}
}
}
Expand Down
Loading

0 comments on commit 943b262

Please sign in to comment.