Commit 7da7983f authored by Carola Nitz's avatar Carola Nitz

DeviceMotion: resolved jumping & not sensitive enough panning

lastEuler.pitch was accidentally set twice which resulted in jumping and
removing the screenscale + adjusting the zoomfactor makes panning and zooming more responsive

(cherry picked from commit af887ffa)
parent df0d5046
From b124ee92f2b4ef6c44515c19d72f0cd2434ae386 Mon Sep 17 00:00:00 2001
From: Adrien Maglo <magsoft@videolan.org>
Date: Wed, 7 Mar 2018 13:45:01 +0100
Subject: [PATCH] DeviceMotion: use quaternions and convert them ourself
Do not use the Euler angles provided by CoreMotion as they are not defined in the same reference frame as libvlc.
---
Sources/DeviceMotion.swift | 82 +++++++++++++++++++++++++++++++++++++---------
1 file changed, 67 insertions(+), 15 deletions(-)
diff --git a/Sources/DeviceMotion.swift b/Sources/DeviceMotion.swift
index 4666801f..2ad61639 100644
--- a/Sources/DeviceMotion.swift
+++ b/Sources/DeviceMotion.swift
@@ -21,6 +21,12 @@ protocol DeviceMotionDelegate:NSObjectProtocol {
}
+struct EulerAngles {
+ var yaw: Double = 0
+ var pitch: Double = 0
+ var roll: Double = 0
+}
+
@objc(VLCDeviceMotion)
class DeviceMotion:NSObject {
@@ -28,31 +34,77 @@ class DeviceMotion:NSObject {
var referenceAttitude:CMAttitude? = nil
@objc weak var delegate: DeviceMotionDelegate? = nil
+
+ func multQuaternion(q1: CMQuaternion, q2: CMQuaternion) -> CMQuaternion {
+ var ret = CMQuaternion()
+
+ ret.x = q1.x * q2.x - q1.y * q2.y - q1.z * q2.z - q1.w * q2.w
+ ret.y = q1.x * q2.y + q1.y * q2.x + q1.z * q2.w - q1.w * q2.z
+ ret.z = q1.x * q2.z + q1.z * q2.x - q1.y * q2.w + q1.w * q2.y
+ ret.w = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y
+
+ return ret
+ }
+
+ func quaternionToEuler(qIn: CMQuaternion) -> EulerAngles {
+ // Change the axes
+ var q = CMQuaternion(x:qIn.y, y:qIn.z, z:qIn.x, w:qIn.w)
+
+ // Rotation of 90°
+ let sqrt2 = 0.707106781186548
+ let qRot = CMQuaternion(x: 0, y: 0, z: -sqrt2 / 2, w: sqrt2 / 2)
+
+ // Perform the rotation
+ q = multQuaternion(q1:qRot, q2:q)
+
+ // Now, we can perform the conversion and manage ourself the singularities
+
+ let sqx = q.x * q.x
+ let sqy = q.y * q.y
+ let sqz = q.z * q.z
+ let sqw = q.w * q.w
+
+ let unit = sqx + sqy + sqz + sqw // if normalised is one, otherwise is correction factor
+ let test = q.x * q.y + q.z * q.w
+
+ var vp = EulerAngles()
+
+ if (test > 0.499 * unit) {
+ // singularity at north pole
+ vp.yaw = 2 * atan2(q.x, q.w)
+ vp.pitch = Double.pi / 2
+ vp.roll = 0
+ } else if (test < -0.499 * unit) {
+ // singularity at south pole
+ vp.yaw = -2 * atan2(q.x, q.w)
+ vp.pitch = -Double.pi / 2
+ vp.roll = 0
+ } else {
+ vp.yaw = atan2(2 * q.y * q.w - 2 * q.x * q.z, sqx - sqy - sqz + sqw)
+ vp.pitch = asin(2 * test / unit)
+ vp.roll = atan2(2 * q.x * q.w - 2 * q.y * q.z, -sqx + sqy - sqz + sqw)
+ }
+
+ vp.yaw = -vp.yaw * 180 / Double.pi
+ vp.pitch = vp.pitch * 180 / Double.pi
+ vp.roll = vp.roll * 180 / Double.pi
+
+ return vp
+ }
+
@objc func startDeviceMotion() {
if motion.isDeviceMotionAvailable {
motion.gyroUpdateInterval = 1.0 / 60.0 // 60 Hz
- motion.startDeviceMotionUpdates(using: .xTrueNorthZVertical, to: .main) {
+ motion.startDeviceMotionUpdates(using: .xArbitraryZVertical, to: .main) {
[weak self] (data, error) in
guard let strongSelf = self, let data = data else {
return
}
- //We're using the initial angle of phone as 0.0.0 reference for all axis
- //we need to create a copy here, otherwise we just have a reference which is being changed in the next line
- if strongSelf.referenceAttitude == nil {
- strongSelf.referenceAttitude = data.attitude.copy() as? CMAttitude
+ if let euler = self?.quaternionToEuler(qIn: data.attitude.quaternion) {
+ strongSelf.delegate?.deviceMotionHasAttitude(deviceMotion:strongSelf, pitch:euler.pitch, yaw:euler.yaw, roll:euler.roll)
}
- // this line basically substracts the reference attitude so that we have yaw, pitch and roll changes in
- // relation to the very first angle
- data.attitude.multiply(byInverseOf: strongSelf.referenceAttitude!)
-
- let pitch = -(180/Double.pi)*data.attitude.pitch // -90; 90
- let yaw = -(180/Double.pi)*data.attitude.yaw // -180; 180
- let roll = -(180/Double.pi)*data.attitude.roll// -180; 180
-
- //print(pitch,yaw,roll)
- strongSelf.delegate?.deviceMotionHasAttitude(deviceMotion:strongSelf, pitch:pitch, yaw:yaw, roll:roll)
}
}
}
--
2.13.0
......@@ -15,9 +15,9 @@ import Foundation
import CoreMotion
@objc(VLCDeviceMotionDelegate)
protocol DeviceMotionDelegate:NSObjectProtocol {
protocol DeviceMotionDelegate: NSObjectProtocol {
func deviceMotionHasAttitude(deviceMotion:DeviceMotion, pitch:Double, yaw:Double, roll:Double)
func deviceMotionHasAttitude(deviceMotion: DeviceMotion, pitch: Double, yaw: Double, roll: Double)
}
......@@ -28,12 +28,12 @@ struct EulerAngles {
}
@objc(VLCDeviceMotion)
class DeviceMotion:NSObject {
class DeviceMotion: NSObject {
let motion = CMMotionManager()
let sqrt2 = 0.5.squareRoot()
var lastEulerAngle = EulerAngles()
var lastQuaternion: CMQuaternion? = nil
var lastEulerAngle: EulerAngles? = nil
var beginningQuaternion: CMQuaternion? = nil
@objc weak var delegate: DeviceMotionDelegate? = nil
......@@ -70,12 +70,12 @@ class DeviceMotion:NSObject {
var vp = EulerAngles()
if (test > 0.499 * unit) {
if test > 0.499 * unit {
// singularity at north pole
vp.yaw = 2 * atan2(q.x, q.w)
vp.pitch = Double.pi / 2
vp.roll = 0
} else if (test < -0.499 * unit) {
} else if test < -0.499 * unit {
// singularity at south pole
vp.yaw = -2 * atan2(q.x, q.w)
vp.pitch = -Double.pi / 2
......@@ -101,32 +101,43 @@ class DeviceMotion:NSObject {
guard let strongSelf = self, let data = data else {
return
}
var euler = strongSelf.quaternionToEuler(qIn: data.attitude.quaternion)
if let lastQuaternion = strongSelf.lastQuaternion {
let lastEuler = strongSelf.quaternionToEuler(qIn: lastQuaternion)
let diffYaw = euler.yaw - lastEuler.yaw
let diffPitch = euler.pitch - lastEuler.pitch
let diffRoll = euler.roll - lastEuler.roll
euler.yaw = strongSelf.lastEulerAngle.yaw + diffYaw
euler.pitch = strongSelf.lastEulerAngle.pitch + diffPitch
euler.pitch = strongSelf.lastEulerAngle.roll + diffRoll
//get the first quaternion that we started with
if strongSelf.beginningQuaternion == nil {
strongSelf.beginningQuaternion = data.attitude.quaternion
}
strongSelf.delegate?.deviceMotionHasAttitude(deviceMotion:strongSelf, pitch:euler.pitch, yaw:euler.yaw, roll:euler.roll)
var currentEuler = strongSelf.quaternionToEuler(qIn: data.attitude.quaternion)
// if we panned we will have a lastEuler value that we need to take as beginning angle
if let lastEulerAngle = strongSelf.lastEulerAngle {
//we get the devicemotion diff between start and currentangle
let beginningEuler = strongSelf.quaternionToEuler(qIn: strongSelf.beginningQuaternion!)
let diffYaw = currentEuler.yaw - beginningEuler.yaw
let diffPitch = currentEuler.pitch - beginningEuler.pitch
let diffRoll = currentEuler.roll - beginningEuler.roll
//and add that to the angle that we had after we lifted our finger
currentEuler.yaw = lastEulerAngle.yaw + diffYaw
currentEuler.pitch = lastEulerAngle.pitch + diffPitch
currentEuler.roll = lastEulerAngle.roll + diffRoll
}
strongSelf.delegate?.deviceMotionHasAttitude(deviceMotion:strongSelf, pitch:currentEuler.pitch, yaw:currentEuler.yaw, roll:currentEuler.roll)
}
}
}
@objc func lastAngle(yaw:Double, pitch:Double, roll:Double) {
lastEulerAngle.yaw = yaw
lastEulerAngle.pitch = pitch
lastEulerAngle.roll = roll
@objc func lastAngle(yaw: Double, pitch: Double, roll: Double) {
if lastEulerAngle == nil {
lastEulerAngle = EulerAngles()
}
lastEulerAngle?.yaw = yaw
lastEulerAngle?.pitch = pitch
lastEulerAngle?.roll = roll
}
@objc func stopDeviceMotion() {
if motion.isDeviceMotionActive {
lastQuaternion = motion.deviceMotion?.attitude.quaternion
beginningQuaternion = nil
lastEulerAngle = nil
motion.stopDeviceMotionUpdates()
}
}
......
......@@ -42,7 +42,7 @@
#define BACKWARD_SWIPE_DURATION 10
#define SHORT_JUMP_DURATION 10
#define ZOOM_SENSITIVITY 2.99f
#define ZOOM_SENSITIVITY 5.f
#define DEFAULT_FOV 80.f
#define MAX_FOV 150.f
#define MIN_FOV 20.f
......@@ -237,8 +237,7 @@ typedef NS_ENUM(NSInteger, VLCPanType) {
[self setupControlPanel];
CGRect screenBounds = [[UIScreen mainScreen] bounds];
CGFloat screenScale = [[UIScreen mainScreen] scale];
_screenPixelSize = CGSizeMake(screenBounds.size.width * screenScale, screenBounds.size.height * screenScale);
_screenPixelSize = CGSizeMake(screenBounds.size.width, screenBounds.size.height);
[self setupConstraints];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment