Skip to content

Instantly share code, notes, and snippets.

@alanf
Last active March 14, 2018 17:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alanf/81891fd7e99edba5ee4569ddc6731eea to your computer and use it in GitHub Desktop.
Save alanf/81891fd7e99edba5ee4569ddc6731eea to your computer and use it in GitHub Desktop.
// Set a new `value` and a the `interpolatedSignal` will emit a number of values between our current `value` and the new `value`.
import Foundation
import ReactiveKit
class InterpolatedFloat {
private let bag = DisposeBag()
let interpolationPeriod: TimeInterval
let interpolationSteps: Int
private var oldValue: Float = 0.0
init(interpolationPeriod: TimeInterval = 0.4,
interpolationSteps: Int = 4,
createTimedSignal: @escaping (Float, Double, DispatchQueue) -> Signal<Float, ReactiveKit.NoError> = SafeSignal<Float>.timer) {
self.interpolationPeriod = interpolationPeriod
self.interpolationSteps = interpolationSteps
_value.skip(first: 1).observeOn(DispatchQueue.main).observeNext { [weak self] newValue in
guard let `self` = self else { return }
for i in 1...self.interpolationSteps {
let increment = (newValue - self.oldValue) / Float(interpolationSteps) * Float(i)
let next = self.oldValue + increment
let time = self.interpolationPeriod / TimeInterval(self.interpolationSteps) * TimeInterval(i)
let timedSignal = createTimedSignal(next, time, DispatchQueue(label: "com.HaloSport.interpolatedFloat"))
timedSignal.bind(to: self.interpolated)
}
self.oldValue = newValue
}.dispose(in: bag)
}
private var _value = Property<Float>(0)
var value: Float {
set {
_value.value = newValue
} get {
return _value.value
}
}
private var interpolated = Property<Float>(0)
var interpolatedSignal: Signal1<Float> {
return interpolated.toSignal()
}
}
import XCTest
class InterpolatedSignalTests: XCTestCase {
func testSettingValues() {
let signal = InterpolatedFloat(interpolationPeriod: 1.0, interpolationSteps: 10)
func testSettingValue(startingValue: Float, newValue: Float, extra: String = "a") {
let expect = expectation(description: "completed signal")
var expected: Float = startingValue
signal.interpolatedSignal.observeNext { next in
debugPrint("next \(next) expected \(expected) \(extra)")
XCTAssertEqual(expected, next, accuracy: 0.000_001)
expected += (newValue - startingValue) / Float(signal.interpolationSteps)
if next == newValue {
expect.fulfill()
}
}.dispose(in: bag)
signal.value = newValue
}
testSettingValue(startingValue: 0.0, newValue: 100.0)
waitForExpectations(timeout: 2.0, handler: nil)
bag.dispose()
testSettingValue(startingValue: 100.0, newValue: 20.0, extra: "b")
waitForExpectations(timeout: 2.0, handler: nil)
bag.dispose()
testSettingValue(startingValue: 20.0, newValue: 0.0, extra: "c")
waitForExpectations(timeout: 2.0, handler: nil)
bag.dispose()
testSettingValue(startingValue: 0.0, newValue: 1.0, extra: "d")
waitForExpectations(timeout: 2.0, handler: nil)
bag.dispose()
}
func testTiming() {
var times: [Double] = []
func timerFunction(element: Float, time: Double, q: DispatchQueue) -> Signal<Float, ReactiveKit.NoError> {
times.append(time)
return Signal1<Float>.just(element)
}
let signal = InterpolatedFloat(interpolationPeriod: 1.0, interpolationSteps: 10, createTimedSignal: timerFunction)
signal.value = 100.0
let expected = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
XCTAssertEqual(times.count, expected.count)
for i in 0..<expected.count {
XCTAssertEqual(times[i], expected[i], accuracy: 0.000_000_1)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment