diff --git a/NYT360Video.podspec b/NYT360Video.podspec index 6af3534..2eb35f1 100644 --- a/NYT360Video.podspec +++ b/NYT360Video.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'NYT360Video' - s.version = '0.6.0' + s.version = '0.6.1' s.summary = 'NYT360Video plays 360ยบ video streamed from an AVPlayer.' s.description = <<-DESC diff --git a/NYT360VideoExample/Info.plist b/NYT360VideoExample/Info.plist index 7f3057e..616a369 100644 --- a/NYT360VideoExample/Info.plist +++ b/NYT360VideoExample/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.6.0 + 0.6.1 CFBundleSignature ???? CFBundleVersion diff --git a/NYT360VideoTests/Info.plist b/NYT360VideoTests/Info.plist index 3b73345..32bebf8 100644 --- a/NYT360VideoTests/Info.plist +++ b/NYT360VideoTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 0.6.0 + 0.6.1 CFBundleSignature ???? CFBundleVersion diff --git a/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m b/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m index ff439ae..4df5f5a 100644 --- a/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m +++ b/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m @@ -135,8 +135,28 @@ - (void)testItCalculatesTheOptimalYFovForAVarietyOfInputs { - (void)testItCalculatesTheCorrectCompassAngleForAVarietyOfInputs { + // Unless the host application needs a non-zero reference angle (zero is the + // default), we want an input camera rotation of 0 to yield a compass angle + // of 0 where 0 is pointing "due north". + // + // The y component of the SCNVector3 is positive in the counter-clockwise + // direction, whereas UIKit rotation transforms are positive in the + // clockwise direction. Thus we want to map camera rotation values to + // compass angle values by mapping them to the equivalent rotation transform + // in the opposite direction. + // + // For example, a positive quarter turn of the camera is equivalent to a + // negative quarter turn of a rotation transform, or: + // + // 0.25 cam rotations -> -0.25 rotation transform rotations + // + // or in their raw radian values: + // + // 1.571 camera radians -> -1.571 rotation transform radians + SCNVector3 eulerAngles; float pi = M_PI; + float oneRotation = pi * 2.0; float compassAngle; float referenceAngle; @@ -144,63 +164,63 @@ - (void)testItCalculatesTheCorrectCompassAngleForAVarietyOfInputs { eulerAngles.y = 0; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); - XCTAssertEqualWithAccuracy(compassAngle, pi, 0.001); + XCTAssertEqualWithAccuracy(compassAngle, 0, 0.001); - eulerAngles.y = pi; + eulerAngles.y = oneRotation; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); XCTAssertEqualWithAccuracy(compassAngle, 0, 0.001); - eulerAngles.y = -pi; + eulerAngles.y = -oneRotation; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); XCTAssertEqualWithAccuracy(compassAngle, 0, 0.001); - eulerAngles.y = pi * 0.5; + eulerAngles.y = oneRotation * -0.5; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); - XCTAssertEqualWithAccuracy(compassAngle, pi * 1.5, 0.001); + XCTAssertEqualWithAccuracy(compassAngle, oneRotation * 0.5, 0.001); - eulerAngles.y = pi * -0.5; + eulerAngles.y = oneRotation * 0.5; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); - XCTAssertEqualWithAccuracy(compassAngle, pi * 0.5, 0.001); + XCTAssertEqualWithAccuracy(compassAngle, oneRotation * -0.5, 0.001); - eulerAngles.y = pi * 1.5; + eulerAngles.y = oneRotation * -1.5; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); - XCTAssertEqualWithAccuracy(compassAngle, pi * 0.5, 0.001); + XCTAssertEqualWithAccuracy(compassAngle, oneRotation * 0.5, 0.001); - eulerAngles.y = pi * -1.5; + eulerAngles.y = oneRotation * 1.5; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); - XCTAssertEqualWithAccuracy(compassAngle, pi * -0.5, 0.001); + XCTAssertEqualWithAccuracy(compassAngle, oneRotation * -0.5, 0.001); - eulerAngles.y = pi * 2.0; + eulerAngles.y = oneRotation * -2.0; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); - XCTAssertEqualWithAccuracy(compassAngle, pi, 0.001); + XCTAssertEqualWithAccuracy(compassAngle, 0, 0.001); - eulerAngles.y = pi * -2.0; + eulerAngles.y = oneRotation * 2.0; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); - XCTAssertEqualWithAccuracy(compassAngle, -pi, 0.001); + XCTAssertEqualWithAccuracy(compassAngle, 0, 0.001); - eulerAngles.y = pi * 2.5; + eulerAngles.y = oneRotation * -2.5; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); - XCTAssertEqualWithAccuracy(compassAngle, pi * 1.5, 0.001); + XCTAssertEqualWithAccuracy(compassAngle, oneRotation * 0.5, 0.001); - eulerAngles.y = pi * -2.5; + eulerAngles.y = oneRotation * 2.5; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); - XCTAssertEqualWithAccuracy(compassAngle, pi * -1.5, 0.001); + XCTAssertEqualWithAccuracy(compassAngle, oneRotation * -0.5, 0.001); - eulerAngles.y = pi * 3.0; + eulerAngles.y = oneRotation * 3.0; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); XCTAssertEqualWithAccuracy(compassAngle, 0, 0.001); - eulerAngles.y = pi * -3.0; + eulerAngles.y = oneRotation * -3.0; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); XCTAssertEqualWithAccuracy(compassAngle, 0, 0.001); - eulerAngles.y = pi * 4.0; + eulerAngles.y = oneRotation * -3.5; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); - XCTAssertEqualWithAccuracy(compassAngle, pi, 0.001); + XCTAssertEqualWithAccuracy(compassAngle, oneRotation * 0.5, 0.001); - eulerAngles.y = pi * -4.0; + eulerAngles.y = oneRotation * 3.5; compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); - XCTAssertEqualWithAccuracy(compassAngle, -pi, 0.001); + XCTAssertEqualWithAccuracy(compassAngle, oneRotation * -0.5, 0.001); } @end diff --git a/Sources/Info.plist b/Sources/Info.plist index 63c3e67..5b6037d 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.6.0 + 0.6.1 CFBundleSignature ???? CFBundleVersion diff --git a/Sources/NYT360EulerAngleCalculations.h b/Sources/NYT360EulerAngleCalculations.h index c8912d7..d55d1aa 100644 --- a/Sources/NYT360EulerAngleCalculations.h +++ b/Sources/NYT360EulerAngleCalculations.h @@ -33,4 +33,19 @@ NYT360EulerAngleCalculationResult NYT360PanGestureChangeCalculation(CGPoint posi CGFloat NYT360OptimalYFovForViewSize(CGSize viewSize); +/** + * Unless the host application needs a non-zero reference angle (zero is the default), we want an input camera rotation of 0 to yield a compass angle of 0 where 0 is pointing "due north". + * + * The y component of the SCNVector3 is positive in the counter-clockwise direction, whereas UIKit rotation transforms are positive in the clockwise direction. Thus we want to map camera rotation values to compass angle values by mapping them to the equivalent rotation transform in the opposite direction. + * + * For example, a positive quarter turn of the camera is equivalent to a negative quarter turn of a rotation transform, or: + * + * 0.25 cam rotations -> -0.25 rotation transform rotations + * + * or in their raw radian values: + * + * 1.571 camera radians -> -1.571 rotation transform radians + * + * Input values in excess of one rotation will be mapped to an equivalent value within the range of plus or minus one radian, such that output values will exceed one rotation. Input values equal (or very very close to equal) to a multiple of one radian (positive or negative) will be mapped to 0. + */ float NYT360CompassAngleForEulerAngles(SCNVector3 eulerAngles, float referenceAngle); diff --git a/Sources/NYT360EulerAngleCalculations.m b/Sources/NYT360EulerAngleCalculations.m index 7037b94..24634c4 100644 --- a/Sources/NYT360EulerAngleCalculations.m +++ b/Sources/NYT360EulerAngleCalculations.m @@ -11,7 +11,7 @@ #pragma mark - Constants CGFloat const NYT360EulerAngleCalculationNoiseThresholdDefault = 0.12; -float const NYT360EulerAngleCalculationDefaultReferenceCompassAngle = M_PI; +float const NYT360EulerAngleCalculationDefaultReferenceCompassAngle = 0; #pragma mark - Inline Functions @@ -156,5 +156,5 @@ CGFloat NYT360OptimalYFovForViewSize(CGSize viewSize) { } float NYT360CompassAngleForEulerAngles(SCNVector3 eulerAngles, float referenceAngle) { - return NYT360UnitRotationForCameraRotation(eulerAngles.y + referenceAngle); + return NYT360UnitRotationForCameraRotation((-1.0 * eulerAngles.y) + referenceAngle); } diff --git a/Sources/NYT360PlayerScene.m b/Sources/NYT360PlayerScene.m index 4429529..599be58 100644 --- a/Sources/NYT360PlayerScene.m +++ b/Sources/NYT360PlayerScene.m @@ -190,7 +190,20 @@ - (void)pause { else { // Prior to iOS 10, SceneKit prefers to use `setPaused:` alone to toggle // playback on a video node. Mimic this usage here to ensure consistency - // and avoid putting the player into an out-of-sync state. + // and avoid putting the player into an out-of-sync state. There is one + // caveat, however: when the host application is pausing playback as the + // app is entering the background (and if background audio is enabled in + // the host application), then if you do not also call `pause` directly + // on the AVPlayer, then the player's audio will continue playing in the + // background. Even though this bug workaround means that our `pause` + // implementation is identical between iOS 8/9 and iOS 10, it's worth + // keeping the above check for the OS version since the edge cases + // before and after iOS 10 are different enough that it's worth keeping + // the implementations separated, if only for clarity that can be added + // via OS specific documentation (and also the fact that iOS 10's + // behavior may change between the current beta and a future production + // release). + [self.player pause]; self.videoNode.paused = YES; } diff --git a/Sources/NYT360ViewController.h b/Sources/NYT360ViewController.h index 46d1fd6..2b4b76f 100644 --- a/Sources/NYT360ViewController.h +++ b/Sources/NYT360ViewController.h @@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN * Called when the compass angle is updated. * * @param viewController The view controller that updated the angle. - * @param compassAngle The current compass angle. + * @param compassAngle The current compass angle. The value will be within the range of plus or minus one radian, non-inclusive, where a positive value is equivalent to a clockwise rotation. * * @note This method is called synchronously from SCNSceneRendererDelegate; its implementation should return quickly to avoid performance implications. */ @@ -62,7 +62,9 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Camera Movement /** - Returns the current compass angle. + * Returns the current compass angle. + * + * The value will be within the range of plus or minus one radian, non-inclusive, where a positive value is equivalent to a clockwise rotation. */ @property (nonatomic, readonly) float compassAngle; diff --git a/Sources/NYT360ViewController.m b/Sources/NYT360ViewController.m index 0b90cde..ab02bb9 100644 --- a/Sources/NYT360ViewController.m +++ b/Sources/NYT360ViewController.m @@ -67,6 +67,10 @@ - (instancetype)initWithAVPlayer:(AVPlayer *)player motionManager:(id