Create UIBezierPath shape in 3D world ARKit

I'm making an app where the user can create some flat shapes by positioning some points on a 3D space with ARKit, but it seems that the part where I create the UIBezierPath using these points is problematic.



In my app, the user starts by positioning a virtual transparent wall in AR at the same place that his device by pressing a button:



        guard let currentFrame = sceneView.session.currentFrame else {
            return
        }
       
        let imagePlane = SCNPlane(width: sceneView.bounds.width, height: sceneView.bounds.height)
        imagePlane.firstMaterial?.diffuse.contents = UIColor.black
        imagePlane.firstMaterial?.lightingModel = .constant
       
        var windowNode = SCNNode()
        windowNode.geometry = imagePlane
        sceneView.scene.rootNode.addChildNode(windowNode)
       
        windowNode.simdTransform = currentFrame.camera.transform
        windowNode.opacity = 0.1



Then, the user place some points (some sphere nodes) on that wall to determine the shape of the flat object that he wants to create by pressing a button. If the user points back to the first sphere node created I close the shape, create a node of it and place it at the same position that the wall:



        let hitTestResult = sceneView.hitTest(self.view.center, options: nil)
       
        if let firstHit = hitTestResult.first {
            if firstHit.node == windowNode {
                let x = Double(firstHit.worldCoordinates.x)
                let y = Double(firstHit.worldCoordinates.y)
                let pointCoordinates = CGPoint(x: x , y: y)
               
                let sphere = SCNSphere(radius: 0.02)
                sphere.firstMaterial?.diffuse.contents = UIColor.white
                sphere.firstMaterial?.lightingModel = .constant
               
                let sphereNode = SCNNode(geometry: sphere)
                sceneView.scene.rootNode.addChildNode(sphereNode)
                sphereNode.worldPosition = firstHit.worldCoordinates
               
                if points.isEmpty {
                    windowPath.move(to: pointCoordinates)
                } else {
                    windowPath.addLine(to: pointCoordinates)
                }
               
                points.append(sphereNode)
               
                if undoButton.alpha == 0 {
                    undoButton.alpha = 1
                }
            } else if firstHit.node == points.first {
                windowPath.close()
                let windowShape = SCNShape(path: windowPath, extrusionDepth: 0)
                windowShape.firstMaterial?.diffuse.contents = UIColor.white
                windowShape.firstMaterial?.lightingModel = .constant
                let tintedWindow = SCNNode(geometry: windowShape)
               
                let worldPosition = windowNode.worldPosition


                tintedWindow.worldPosition = worldPosition
                sceneView.scene.rootNode.addChildNode(tintedWindow)
               
                //removing all the sphere nodes from points and reinitializing the UIBezierPath windowPath
                removeAllPoints()
            }
        }


That code works when I create a first invisible wall and a first shape, but when I create a second wall, when I'm done to draw my shape, the shape appears to be deformed and not at the right place like really not at the right place at all. So I think that I'm missing something with the coordinates of my UIBezierPath points but what ?

Accepted Reply

I just found the problem ! I was just using the wrong vectors and coordinates... I've never been a math/geometry guy haha


So instead of using:

let x = Double(firstHit.worldCoordinates.x) 
let y = Double(firstHit.worldCoordinates.y)


I now use:

let x = Double(firstHit.localCoordinates.x)
let y = Double(firstHit.localCoordinates.y)


And instead of using:

let worldPosition = windowNode.worldPosition


I now use:

let worldPosition = windowNode.transform


That's why the position of my shape node was depending of the initialisation of the AR session, I was working with world coordinates, seems obvious to me now.

Replies

Ok so after several tests, it seems that it depends on the orientation of the device at the launch of the AR session. When the device, at launch, faces the first wall that the user will create, the shape is created and places as expected. But if the user for exemple launch the app with his device pointed in one direction, then do a rotation of 90 degrees on himself, place the first wall and create his shape, the shape will be deformed and not at the right place.


So it seems that it's a problem of 3D coordinates but I still don't figure it out...

I just found the problem ! I was just using the wrong vectors and coordinates... I've never been a math/geometry guy haha


So instead of using:

let x = Double(firstHit.worldCoordinates.x) 
let y = Double(firstHit.worldCoordinates.y)


I now use:

let x = Double(firstHit.localCoordinates.x)
let y = Double(firstHit.localCoordinates.y)


And instead of using:

let worldPosition = windowNode.worldPosition


I now use:

let worldPosition = windowNode.transform


That's why the position of my shape node was depending of the initialisation of the AR session, I was working with world coordinates, seems obvious to me now.