Search code examples
swiftscenekitarkit

SCNVector3 to SCNVector3 in 2D arrow direction


I am working with ARKit

I have the camera x,y,z (cartesian coordinates) and a 3D object x,y,z. I am trying to point an arrow (UIImageView) in 2D (x,y) to show where the camera should move so the user will see the 3D object.

I have tried the code below to calculate the arrow's direction using the arctangent function (the vector's direction) but i suspect that there is a problem with the coordinate system

func redirectArrow(bee: SCNNode, arrow: UIImageView) {
    
    let (direction, position) = self.getUserVector()

    let dx = bee.position.x - position.x
    let dy = bee.position.y - position.y

    let arctangent = atan(dy/dx)

    arrow.transform = CGAffineTransform(rotationAngle: CGFloat(arctangent))
    
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        self.redirectArrow(bee: bee, arrow: arrow)
    }
    
}

The code for getUserVector()

func getUserVector() -> (SCNVector3, SCNVector3) { // (direction, position)
    if let frame = self.sceneView.session.currentFrame {
        let mat = SCNMatrix4(frame.camera.transform) // 4x4 transform matrix describing camera in world space
        let dir = SCNVector3(-1 * mat.m31, -1 * mat.m32, -1 * mat.m33) // orientation of camera in world space
        let pos = SCNVector3(mat.m41, mat.m42, mat.m43) // location of camera in world space
        
        return (dir, pos)
    }
    return (SCNVector3(0, 0, -1), SCNVector3(0, 0, -0.2))
}

UPDATE

Thanks to mnuages answer the arrow now is directed the 3D object using the code below

func redirectArrow(bee: SCNNode, arrow: UIImageView) {
    
    let (direction, position) = self.getUserVector()
    
    let bee2DPosition = sceneView.projectPoint(bee.position)
    let world2DPosition = sceneView.projectPoint(direction)
    
    let dx = bee2DPosition.x - world2DPosition.x
    let dy = bee2DPosition.y - world2DPosition.y

    var arctangent: Float = atan(dy/dx)
    
    if dx < 0 {
        arctangent += Float(Double.pi)
    }

    arrow.transform = CGAffineTransform(rotationAngle: CGFloat(arctangent))
    
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        self.redirectArrow(bee: bee, arrow: arrow)
    }
    
}

Solution

  • The projectPoint(_:) API is what you need:

    Projects a point from the 3D world coordinate system of the scene to the 2D pixel coordinate system of the renderer.