ARKit – Measure distances with augmented reality

Today a tutorial about augmented reality, in particular the use of the new Apple framework, ARKit, to measure distances between two points in a 3D virtual space.

Final result: ‹26 cm distance between the two green points.

How to?

First of all, as usual, you need to create a new XCode project. Select the “Augmented Reality App” type:

Open your ViewController.swift and remove the useless stuff, like:

let scene = SCNScene(named: "art.scnassets/ship.scn")!
sceneView.scene = scene 

and all the ARSCNViewDelegate methods.


Well, your code is now clear!

You need to add now two variables, an array of SCNNode (the dots) and another SCNNode that is the text displayed on the screen.

Second thing is to add the touch handler, in this case, touchedBegan in order to add points:

var dotNodes = [SCNNode]()
var textNode = SCNNode()

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { } 

In the touchBegan, you need to recognize the number of touches, and add the green dots:

override func touchesBegan(_ touches: Set, with event: UIEvent?) {
 if dotNodes.count >= 2 { resetDots() }

 if let touchLocation = touches.first?.location(in: sceneView) {
      let hitTestResults = sceneView.hitTest(touchLocation, types: .featurePoint)
      if let hitResult = hitTestResults.first {
          addDot(at: hitResult)
      }
  }
 } 

Your addDot function, append on the main AR scene a child node, the dot, based on the user touch location.

 func addDot( at hitResult: ARHitTestResult ) {
     // the size of the green circle
     let dotGeometry = SCNSphere(radius: 0.005)
     
     let material = SCNMaterial()
     material.diffuse.contents = UIColor.systemGreen
     
     dotGeometry.materials = [material]

     // the "dot" element
     let dotNode = SCNNode(geometry: dotGeometry)
     dotNode.position = SCNVector3(
         hitResult.worldTransform.columns.3.x,
         hitResult.worldTransform.columns.3.y,
         hitResult.worldTransform.columns.3.z)
     
     sceneView.scene.rootNode.addChildNode(dotNode)
     
     dotNodes.append(dotNode)

     // we have 2 points on scree, try to calculate distance
     if dotNodes.count >= 2 {
         calculateDistance()
     }
 } 

and last one, calculateDistance calculate distance between points:

 func calculateDistance() {
     let start = dotNodes.first!
     let end = dotNodes.last!
     
     var distance = sqrt(
         pow(end.position.x - start.position.x, 2) +
         pow(end.position.y - start.position.y, 2) +
         pow(end.position.z - start.position.z, 2)
     )
     
     // convert to cm
     distance *= 100
 
     let distanceFormatted = String(format: "%.2f cm", abs(distance))
     updateText(text: distanceFormatted, atPosition: end.position)
 } 

Cool! We have a distance now.


The last cool thing is to write text on screen. We use updateText to draw on screen.

The text on screen works in the same way as the dot. Is a SCNode added on the root scene.

func updateText( text: String, atPosition: SCNVector3 ) {
     textNode.removeFromParentNode()
 
     let textGeometry = SCNText(string: text, extrusionDepth: 1.0)
     textGeometry.firstMaterial?.diffuse.contents = UIColor.systemRed
     
     textNode = SCNNode(geometry: textGeometry)
     textNode.position = SCNVector3(
         atPosition.x,
         atPosition.y + 0.01,
         atPosition.z
     )
 
     textNode.scale = SCNVector3(0.01, 0.01, 0.01)
     sceneView.scene.rootNode.addChildNode(textNode)
 } 

That’s all. It is very easy.


Clear all points:

func resetDots() {
     for dot in dotNodes {
         dot.removeFromParentNode()
     }
     dotNodes = [SCNNode]()
} 

As final result you should see something like this:


Enjoy measuring things!

Alberto Pasca