jesseCampeez
jesseCampeez

Reputation: 107

remove nodes from scene after use ARSCNView

I am making an app using ARKit to measure between two points. The goal is to be able to measure length, store that value, then measure width and store.

The problem I am having is disposing of the nodes after I get the measurement.

Steps so far: 1) added a button with a restartFunction. this worked to reset the measurements but did not remove the spheres from scene, and also made getting the next measurement clunky.

2) set a limit on > 2 nodes. This functionaly works best. But again the spheres just stay floating in the scene.

Here is a screen shot of the best result I have had.

@objc func handleTap(sender: UITapGestureRecognizer) {
    let tapLocation = sender.location(in: sceneView)
    let hitTestResults = sceneView.hitTest(tapLocation, types: .featurePoint)
    if let result = hitTestResults.first {
        let position = SCNVector3.positionFrom(matrix: result.worldTransform)

        let sphere = SphereNode(position: position)


        sceneView.scene.rootNode.addChildNode(sphere)

        let tail = nodes.last

        nodes.append(sphere)

        if tail != nil {
            let distance = tail!.position.distance(to: sphere.position)
            infoLabel.text = String(format: "Size: %.2f inches", distance)

            if nodes.count > 2 {

                nodes.removeAll()

            }
        } else {
            nodes.append(sphere)

        }
    }
}

I am new to Swift (coding in general) and most of my code has come from piecing together tutorials.

Upvotes: 1

Views: 2060

Answers (2)

PongBongoSaurus
PongBongoSaurus

Reputation: 7385

I think the issue here is that your are not actually removing the SCNNodes you have added to hierarchy.

Although you are removing the nodes from what I assume is an array of SCNNodes by calling: nodes.removeAll(), you first need to actually remove them from the scene hierarchy.

So what you need to do is call the following function on any node you wish to remove:

removeFromParentNode()

Which simply:

Removes the node from its parent’s array of child nodes.

As such you would do something like this which first removes the nodes from the hierarchy, and then removes them from the array:

for nodeAdded in nodesArray{
    nodeAdded.removeFromParentNode()
}

nodesArray.removeAll()

So based on the code provided you could do the following:

if nodes.count > 2 {

    for nodeAdded in nodes{
        nodeAdded.removeFromParentNode()
    }

    nodes.removeAll()

}

For future reference, if you want to remove all SCNNodes from you hierarchy you can also call:

self.augmentedRealityView.scene.rootNode.enumerateChildNodes { (existingNode, _) in
    existingNode.removeFromParentNode()
}

Whereby self.augmentedRealityView refers to the variable:

var augmentedRealityView: ARSCNView!

Here is a very basic working example based on (and modified from) the code you have provided:

/// Places A Marker Node At The Desired Tap Point
///
/// - Parameter sender: UITapGestureRecognizer
@objc func handleTap(_ sender: UITapGestureRecognizer) {

    //1. Get The Current Tap Location
    let currentTapLocation = sender.location(in: sceneView)

    //2. Check We Have Hit A Feature Point
    guard let hitTestResult = self.augmentedRealityView.hitTest(currentTapLocation, types: .featurePoint).first else { return }

    //3. Get The World Position From The HitTest Result
    let worldPosition = positionFromMatrix(hitTestResult.worldTransform)

    //4. Create A Marker Node
    createSphereNodeAt(worldPosition)

    //5. If We Have Two Nodes Then Measure The Distance
    if let distance = distanceBetweenNodes(){
        print("Distance == \(distance)")
    }

}

/// Creates A Marker Node
///
/// - Parameter position: SCNVector3
func createSphereNodeAt(_ position: SCNVector3){

    //1. If We Have More Than 2 Nodes Remove Them All From The Array & Hierachy
    if nodes.count >= 2{

        nodes.forEach { (nodeToRemove) in
            nodeToRemove.removeFromParentNode()
        }

        nodes.removeAll()
    }

    //2. Create A Marker Node With An SCNSphereGeometry & Add It To The Scene
    let markerNode = SCNNode()
    let markerGeometry = SCNSphere(radius: 0.01)
    markerGeometry.firstMaterial?.diffuse.contents = UIColor.cyan
    markerNode.geometry = markerGeometry
    markerNode.position = position
    sceneView.scene.rootNode.addChildNode(markerNode)

    //3. Add It To The Nodes Array
    nodes.append(markerNode)
}

/// Converts A matrix_float4x4 To An SCNVector3
///
/// - Parameter matrix: matrix_float4x4
/// - Returns: SCNVector3
func positionFromMatrix(_ matrix: matrix_float4x4) -> SCNVector3{

    return SCNVector3(matrix.columns.3.x, matrix.columns.3.y, matrix.columns.3.z)

}

/// Calculates The Distance Between 2 Nodes
///
/// - Returns: Float?
func distanceBetweenNodes()  -> Float? {

    guard let firstNode = nodes.first, let endNode = nodes.last else { return nil }
    let startPoint = GLKVector3Make(firstNode.position.x, firstNode.position.y, firstNode.position.z)
    let endPoint = GLKVector3Make(endNode.position.x, endNode.position.y, endNode.position.z)
    let distance = GLKVector3Distance(startPoint, endPoint)
    return distance
}

For an example of a measuringApp which might help your development you can look here: ARKit Measuring Example

Hope it helps...

Upvotes: 2

R Bradshaw
R Bradshaw

Reputation: 183

This looks like a logic issue. You're assigning nodes.last to tail just before checking if tail is not nil. So it will never be != nil so you'll never execute the nodes.append(sphere) in the else.

I agree with @dfd. Set a breakpoint to make sure the code is being executed before continuing.

Upvotes: 0

Related Questions