3

I am using ARKit to detect walls at runtime, I use a hit test of type .estimatedVerticalPlane when some point of the screen is touched. I am trying to apply Y rotation to node corresponding to the detected plane orientation.

I want to compute the rotation in :

private func computeYRotationForHitLocation(hitTestResult: ARHitTestResult) -> Float {
    guard hitTestResult.type == .estimatedVerticalPlane else { return 0.0 }
//        guard let planeAnchor = hitTestResult.anchor as? ARPlaneAnchor else { return 0.0 }
//        guard let anchoredNode = sceneView.node(for: planeAnchor) else { return 0.0 }

    let worldTransform = hitTestResult.worldTransform
    let anchorNodeOrientation = ???

    return .pi * anchorNodeOrientation.y
}

How to deduce the anchorNodeOrientation to apply given the wall orientation, this post explains it well for a hit test type that provide an ARAnchor but for estimatedVerticalPlane it is nil. (ARKit 1.5 how to get the rotation of a vertical plane).

Also when I do : po hitTestResult.worldTransform on the debugger it prints a rotation for worldTransform 91 degrees etc but I cannot retrieve it from the transform.

michael-martinez
  • 767
  • 6
  • 24

1 Answers1

3

I finally managed to get the Euler Angles from the transform through the following transformation, still have to check the results correctness :

import SceneKit
import ARKit

public extension matrix_float4x4 {

/// Retrieve translation from a quaternion matrix
public var translation: SCNVector3 {
    get {
        return SCNVector3Make(columns.3.x, columns.3.y, columns.3.z)
    }
}

/// Retrieve euler angles from a quaternion matrix
public var eulerAngles: SCNVector3 {
    get {
        //first we get the quaternion from m00...m22
        //see http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
        let qw = sqrt(1 + self.columns.0.x + self.columns.1.y + self.columns.2.z) / 2.0
        let qx = (self.columns.2.y - self.columns.1.z) / (qw * 4.0)
        let qy = (self.columns.0.z - self.columns.2.x) / (qw * 4.0)
        let qz = (self.columns.1.x - self.columns.0.y) / (qw * 4.0)

        //then we deduce euler angles with some cosines
        //see https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
        // roll (x-axis rotation)
        let sinr = +2.0 * (qw * qx + qy * qz)
        let cosr = +1.0 - 2.0 * (qx * qx + qy * qy)
        let roll = atan2(sinr, cosr)

        // pitch (y-axis rotation)
        let sinp = +2.0 * (qw * qy - qz * qx)
        var pitch: Float
        if fabs(sinp) >= 1 {
            pitch = copysign(Float.pi / 2, sinp)
        } else {
            pitch = asin(sinp)
        }

        // yaw (z-axis rotation)
        let siny = +2.0 * (qw * qz + qx * qy)
        let cosy = +1.0 - 2.0 * (qy * qy + qz * qz)
        let yaw = atan2(siny, cosy)

        return SCNVector3(roll, pitch, yaw)
    }
}
}
michael-martinez
  • 767
  • 6
  • 24
  • Shouldn’t the vector at the end be roll, pitch, yaw ? – BlackMirrorz May 16 '18 at 14:17
  • Probably, we should mimic the order it is set in eulerAngles, I haven't time to check it now, could you confirm so I update the return value ? – michael-martinez May 16 '18 at 14:28
  • I will take a look. I had a quick look and the rotations given matched the anchor ones but not in the correct order unless I did something wr’ong :) – BlackMirrorz May 16 '18 at 14:29
  • Ok I think it looks ok then :) – michael-martinez May 16 '18 at 14:30
  • It’s a great bit of code :) as you’d laid just need to double check the structure of the vector at the end :) – BlackMirrorz May 16 '18 at 14:35
  • Thanks it's quite experimental for now, we should confirm here that for different use cases, this represent the detected wall correctly, that is does the yaw value represent the real yaw angle (most of the time this will be the only useful info to place some plane on it). – michael-martinez May 16 '18 at 14:46
  • @michael-martinez Hi, Can you clarify how this is used? after you call your computeYrotationOnHitLocation you just do hittest.transform.eulerangles? Then you apply the return angle to the geometry you want to place? – mushcraft May 20 '19 at 15:22