Home > Software engineering >  How to track the 2D coordinates of node's vertices?
How to track the 2D coordinates of node's vertices?

Time:01-16

I have a simple plane node that tracks a face.

func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
    guard anchor is ARFaceAnchor else { return nil }
    
    let plane = SCNPlane(width: 0.1, height: 0.2)
    let planeNode = SCNNode(geometry: plane)
    planeNode.geometry?.firstMaterial?.fillMode = .lines
    planeNode.geometry?.firstMaterial?.diffuse.contents = UIColor.blue
    planeNode.name = "Plane Node"
    return planeNode
}

I want to be able to track the coordinates of all four corners of the plane. I'm looking to get the 2D coordinates that are projected on the screen.

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
    guard let faceAnchor = anchor as? ARFaceAnchor else {
        return
    }
    
    node.enumerateChildNodes { childNode, _ in
        guard childNode.name == "Plane Node" else { return }
        let worldPosition = childNode.worldPosition
        let screenPosition = renderer.projectPoint(worldPosition)
        print(CGPoint(x: Int(screenPosition.x), y: Int(screenPosition.y)))
    }
}

Above tracks the center position of the plane, but how do I track the four corner coordinates?

I tried using the width and the height of the plane using the following to calculate the distance from the center coordinate, but I'm unable to get the proper width and the height that I can work with screen position I've obtained for the center coordinate.

extension SCNNode {
    var width: Float {
        return (boundingBox.max.x - boundingBox.min.x) * scale.x
    }
    
    var height: Float {
        return (boundingBox.max.y - boundingBox.min.y) * scale.y
    }
}

CodePudding user response:

It could be probably a solution (woraround) to add i.Ex. invisible childNodes (without geometry) to each of the corners or edges which positions you want to track. Then grab the presentation.worldPosition of each of those nodes at anytime you like, and convert them using your function above to 2D space.

CodePudding user response:

Try the following solution. Despite the fact that it's a macOS project, you can easily implement this idea in your ARKit project.

enter image description here

import SceneKit

class ViewController: NSViewController {
    
    var sceneView: SCNView? = nil
    let planeNode = SCNNode()
    var vertices: [SCNVector3] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        sceneView = self.view as? SCNView
        sceneView?.scene = SCNScene()
        sceneView?.delegate = self
        sceneView?.backgroundColor = .black
        sceneView?.autoenablesDefaultLighting = true
        
        // Plane
        let plane = SCNPlane(width: 0.5, height: 0.5)
        planeNode.name = "planar"
        planeNode.geometry = plane
        planeNode.geometry?.firstMaterial?.isDoubleSided = true
        sceneView?.scene?.rootNode.addChildNode(planeNode)
        planeNode.runAction(SCNAction.move(to: SCNVector3(0.2, 0.2,-0.2), 
                                     duration: 2))            
        self.trackingSpheres()
    }
}

Delegate's method (in your case it's renderer(_:didUpdate:for:) instance method).

extension ViewController: SCNSceneRendererDelegate {
    
    func renderer(_ renderer: SCNSceneRenderer, 
           updateAtTime time: TimeInterval) {
        
        if let spheres = sceneView?.scene?.rootNode.childNodes[0].childNodes {
            
            for (_, sphere) in spheres.enumerated() {
                
                let truncateX = String(format: "%.2f", sphere.worldPosition.x)
                let truncateY = String(format: "%.2f", sphere.worldPosition.y)
                let truncateZ = String(format: "%.2f", sphere.worldPosition.z)
                
                print("\(sphere.name!):", truncateX, truncateY, truncateZ)
            }
        }
    }
}

A method creating four invisible tiny tracking spheres.

extension ViewController { 
  
    fileprivate func trackingSpheres() { 
        // retrieving a plane node from scene
        if let node = sceneView?.scene?.rootNode.childNode(
                                                  withName: "planar",
                                               recursively: true) {
            let left = node.boundingBox.min.x
            let right = node.boundingBox.max.x
            let lower = node.boundingBox.min.y
            let upper = node.boundingBox.max.y
            let south = node.boundingBox.min.z                
            // Counter clock-wise
            let ll = SCNVector3(x: left, y: lower, z: south)
            let lr = SCNVector3(x: right, y: lower, z: south)
            let ur = SCNVector3(x: right, y: upper, z: south)
            let ul = SCNVector3(x: left, y: upper, z: south)
            
            self.vertices  = [ll, lr, ur, ul]
                        
            for i in 1...4 {                    
                let sphereNode = SCNNode(geometry: SCNSphere(radius: 0.01))
                
                sphereNode.position = SCNVector3(vertices[i-1].x,
                                                 vertices[i-1].y,
                                                 vertices[i-1].z) 
                sphereNode.name = "sphere\(i)"
                sphereNode.opacity = 0.0         // 100% transparent

                sphereNode.geometry?.firstMaterial?.diffuse.contents = 
                                                               NSColor.red
                node.addChildNode(sphereNode)
            }
        }
    }
}

enter image description here

  • Related