قالب وردپرس درنا توس
Home / Tips and Tricks / ARKit 101: How to Measure the Distance Between Two Points on a Horizontal Plane in Augmented Reality «Mobile AR News :: Next Reality

ARKit 101: How to Measure the Distance Between Two Points on a Horizontal Plane in Augmented Reality «Mobile AR News :: Next Reality



In our last ARKit tutorial, we learned how to measure the size of horizontal planes. It was a helpful introduction to the arena of determining spatial relationships with real world spaces in comparison to virtual objects and experiences.

This time, we will delve into a slightly different area that touches on another aspect of measurement in augmented reality. [19659003] What will you learn?

In this tutorial, you'll learn how to measure the distance between any two points on a horizontal plane, so you can measure everything on a flat surface with an iPad or iPhone. Using ARKit

If you have If you have not done so yet, have a look at the tutorial below to learn how to measure the size of horizontal layers

Minimum Requirements

Step 1: Download the required assets

To help you understand this tutorial I've created a folder with the required 2D assets and Swift files that are needed for the project. These files make sure that they are not lost in this manual, so download the zipped folder containing the assets and unzip it.

Step 2: Set up the AR project in Xcode

If you're not sure how to do this, follow Step 2 in our post to controlling a 3D layer with hitTest to yours AR project in Xcode. Give your project a different name. B. NextReality_Tutorial5 . Do a quick test before continuing with the following tutorial.

Step 3: Import assets into your project

In the project navigator, click the Assets.xcassets folder. We will add our 2D images here. Then right-click on the left area of ​​the area on the right side of the project navigator. Select "Import" and add the file "overlay_grid.png" from the unzipped folder "Assets".

Next in the Project Navigator Right-click the yellow folder for "NextReality_Tutorial5" (or whatever you named your project to have). Select the option "Add files to & # 39; NextReality_Tutorial5 & # 39; s".

Navigate to the unzipped "Assets" folder and select "Grid.swift" "File." Be careful To copy need to "turn on and leave everything else as it is, then click" Add. "

" Grid .swift "should now be included in your project and your project navigator should look something like this:

This file helps to create a grid for create any horizontal plane that ARKit detects.

Step 4: Placing a Grid to Indicate Detected Horizontal Plains

To quickly learn about ARKit's general detection capabilities, Take a look at our horizontal plane detection tutorial .

Open the ViewController.swift class. Click on it. If you want to follow the final Step 4 code, just open this link to view it on GitHub.

In the ViewController.swift file, change the scene build line in the viewDidLoad () ] method. Change it:

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

To do this, make sure we do not create a scene with the default ship model:

  let scene = SCNScene () 

Next, find this line at the top of the file:

  @IBOutlet var sceneView: ARSCNView ! 

Add this line below this line to create an array of "grids" for all Horizontal levels detected:

  var grids = [Grid] () 

Copy the following two methods, as below listed at the end of the file before the last curly bracket (} ) in the file. With these methods, we can add our grid on the horizontal planes, which were recognized by ARKit as a visual indicator.

  // 1.
func renderer (_ Renderer: SCNSceneRenderer, didAdd Node: SCNNode, for anchor: ARAnchor) {
let grid = Grid (anchor: anchor as! ARPlaneAnchor)
self.grids.append (grid)
node.addChildNode (grid)
}
// 2.
func renderer (_ Renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
let grid = self.grids.filter {grid in
return raster.anchor.identifier == anchor.identifier
}.first

guard let foundGrid = grid else {
return
}

foundGrid.update (anchor: anchor as! ARPlaneAnchor)
} 

Let's talk quickly about what happens with these two methods: [196909039] The didAdd () is called when a new node is inserted into the ARSCNView . Here we take the detected ARPlaneAnchor and add it as our Grid object that adds the grid image we imported to each detected plane.

  • The didUpdate () is called when newer ARPlaneAnchor nodes are detected or when the aircraft is extended. In this case, we also want to bring our network up to date and expand. We do this by calling update () to this special grid .
  • Now we activate feature points. Under this line in viewDidLoad () :

      sceneView.showsStatistics = true 

    Added:

      sceneView.debugOptions = ARSCNDebugOptions.showFeaturePoints 

    Next, we turn on horizontal-level detection , Under this line in viewWillAppear () :

      let configuration = ARWorldTrackingConfiguration () 

    Add:

      configuration.planeDetection = .horizontal 

    This is very important ! This ensures that ARKit can detect horizontal, flat geometric planes in the real world. The feature points allow us to see all the 3D points that ARKit can recognize.

    Run your app on your phone and walk around. Focus on a well-lit area on the floor, and you should be able to see blue grids when a horizontal plane is detected:

    Checkpoint : Your entire project at the end of this step should look like the last Step 4 code on my GitHub.

    Step 5: Use the hitTest to Place Markers on the Detected Horizontal Plains

    When you begin this step, we strongly recommend that you review our [hitTestTutorial for the basics explain how the hit test works. In this step, we place start and end markers between our two points.

    If you want to follow the final step 5 code, just open the link to see it on GitHub.

    First add a gesture recognizer to the end of the viewDidLoad () method, as shown below. As soon as a tap on the screen happens, the method getapped () is called.

      let gesticRecognizer = UITapGestureRecognizer (target: self, action: #selector (tapped))
    sceneView.addGestureRecognizer 

    Now add getapped () as shown below, at the end of the file, but before the last curly bracket (} ).

      @objc func tapped (gesture: UITapGestureRecognizer) {
    // Receive 2D position of the touch event on the screen
    leave touchPosition = gesten.stelle (in: sceneView)
    
    // Translate these 2D points to 3D points with hitTest (existing layer)
    hitTestResults = sceneView.hitTest (touchPosition, types: .existingPlane)
    
    Guard leave hitTest = hitTestResults.first else {
    return
    }
    } 

    Currently, this code translates only the 2D point tapped on the screen into a 3D point on a detected level using hitTest. We will come back to adding more features in a later step.

    We need multiple pairs of points to be added at the horizontal levels. Each pair has a start and end point. To do this, we need to keep track of which point is added based on the order of the taps. The first tap is for the starting point and the second tap is for the end point. After that it will be reset so that more pairs can be added. Let's start with:

    First declare and initialize this variable and track the number of taps immediately after the variable declaration for array Grid that we created in the previous step:

      var numberOfTaps = 0 

    Next, we increment this variable each time a tap passes, or in other words, when tapped () is called. Add this to the beginning of the tapped () method:

      numberOfTaps + = 1 

    Now we need logic to decide if it was the first or second second. Based on this, we will insert a red marker or a green marker. Add this at the end of the method getapped () :

      // If you first type, add a red marker. If you type twice, add a green marker and reset it to 0
    if numberOfTaps == 1 {
    addRedMarker (hitTestResult: hitTest)
    }
    otherwise {
    // After the second tap, reset the taps to 0
    numberOfTaps = 0
    addGreenMarker (hitTestResult: hitTest)
    } 

    In the above code, we made sure that a red marker was added if it was the first, and a green marker if it was the second. Next, we reset the variable numberOfTaps after the second tap to 0 so that new pairs of points can be added.

    Finally, we define the methods that add our markers. Add the following code after the method tipped () but before the last curly-brace in the file:

      func addRedMarker (hitTestResult: ARHitTestResult) {
    addMarker (hitTestResult: hitTestResult, color: .red)
    }
    
    func addGreenMarker (hitTestResult: ARHitTestResult) {
    addMarker (hitTestResult: hitTestResult, color: .green)
    }
    
    func addMarker (hitTestResult: ARHitTestResult, color: UIColor) {
    Leave geometry = SCNSphere (radius: 0.01)
    geometry.firstMaterial? .diffuse.contents = color
    
    let markerNode = SCNNode (geometry: geometry)
    markerNode.position = SCNVector3 (hitTestResult.worldTransform.column.3.x, hitTestResult.worldTransform.column.3.y, hitTestResult.worldTransform.column.3.z)
    
    sceneView.scene.rootNode.addChildNode (markerNode)
    } 

    Here our two methods addRedMarker () and addGreenMarker () call the helper method addMarker () to add both markers. In addMarker () we create a SCNSphere instance with the passed-in color and place it using the hitTest result to identify the point at which we tapped.

    Deploying and Running the App Move your phone around and focus on a well-lit area on the floor. Once you see the detected horizontal planes, tap anywhere to place your red and green markers. You should see something like this:

    Checkpoint : Your entire project should look like the last step at the end of this step 5 code on my GitHub

    Step 6: Draw a line and calculate the distance between the two points

    In this step we will do some simple calculations to calculate the distance between the two added points last step, and use some SceneKit APIs to draw a line between the two.

    If you want to follow the final step 6 code along with just open the link to see it GitHub.

    Okay, now we need to be able to track where our two points are being added. Let's make variables for them. Add the following code after declaring variable numberOfTaps from the previous step:

      var startPoint: SCNVector3!
    var endPoint: SCNVector3! 

    Next, store the values ​​of hitTest results in these variables. After this line in tapped () :

      if numberOfTaps == 1 {

    Add this line:

      startPoint = SCNVector3 (hitTest.worldTransform.column.3.x, hitTest. WorldTransform .column.3.y, hitTest.worldTransform.columns.3.z) 

    Then, after this line in get tapped () :

      // After the second tap put the taps to 0
    numberOfTaps = 0 

    Add this line:

      endPoint = SCNVector3 (hitTest.worldTransform.column.3.x, hitTest.worldTransform.column.3.y, hitTest.worldTransform.column.3.z) [19659030] We simply store the values ​​of the hitTest result in these two variables for each marker. 

    Let's add an extension to the type SCNGeometry . Paste the following code at the beginning of the file just after this line importing ARKit: import ARKit

      extension SCNGeometry {
    Class func lineFrom (vector vector1: SCNVector3, toVector vector2: SCNVector3) -> SCNGeometry {
    leave indices: [Int32] = [0, 1]
    
    leave source = SCNGeometrySource (Vertices: [vector1, vector2])
    let Element = SCNGeometryElement (indices: indices, primitiveType: .line)
    
    return SCNGeometry (Sources: [source] Items: [element])
    }
    } 

    Here we add a custom functionality to SCNGeometry to allow us to draw a line from one point to another.

    Next, after adding the green marker to this line: [19659030] addGreenMarker (hitTestResult: hitTest)

    Add this line of code:

      addLineBetween (start: startPoint, end: endPoint) [19659030] This method helps us to add the line to our  ARSCNView . and show it on the screen. Add  addLineBetween ()  at the end of the file, but before the last curly brace: 

      func addLineBetween (start: SCNVector3, end: SCNVector3) {
    Leave lineGeometry = SCNGeometry.lineFrom (Vector: Start, ToVector: End)
    let lineNode = SCNNode (geometry: lineGeometry)
    
    sceneView.scene.rootNode.addChildNode (row node)
    } 

    This method inherits start and end points in the form of vectors ( SCNVector3 ) and adds the generated line in the form of SCNNode to the scene. In other words, we should now see a line between our two points!

    Next we need a way to calculate the distance between two points. After adding the extension code SCNGeometry in the previous step, add the following code:

      extension SCNVector3 {
    static func distanceFrom (vector vector1: SCNVector3, toVector vector2: SCNVector3) -> Float {
    Let x0 = vector1.x
    Let x1 = vector2.x
    Let y0 = vector1.y
    Leave y1 = vector2.y
    Let z0 = vector1.z
    Let z1 = vector2.z
    
    Return sqrtf (powf (x1-x0, 2) + powf (y1-y0, 2) + powf (z1-z0, 2))
    }
    }
    
    Extension Float {
    func meterToInches () -> Float {
    to give back yourself * 39.3701
    }
    } 

    Here we add two extensions. The first is to add custom functionality to the [SCNVector3 type by allowing us to calculate the distance between the two points using the distance formula. The second feature adds custom functionality to the float type, allowing us to convert meters to inches.

    Next, we add the logic to call our method, which draws a line between the two points in the last step:

      addLineBetween (start: startPoint, end: endPoint) 

    Add this line: [19659029] addDistanceText (distance: SCNVector3.distanceFrom (vector: startPoint, toVector: endPoint), at: endPoint)

      Finally, we add the method  addDistanceText ()  at the end of the file, but before the last curly brace add: 

      func addDistanceText (distance: float, at point: SCNVector3) {
    let textGeometry = SCNText (String: String (format: "% .1f", distance.metersToInches ()), extrusionDepth: 1)
    textGeometry.font = UIFont.systemFont (ofSize: 10)
    textGeometry.firstMaterial? .diffuse.contents = UIColor.black
    
    let textNode = SCNNode (geometry: textGeometry)
    textNode.position = SCNVector3Make (dot.x, dot.y, dot.z);
    textNode.scale = SCNVector3Make (0.005, 0.005, 0.005)
    
    sceneView.scene.rootNode.addChildNode (text node)
    } 

    This method simply outputs the distance between the two points next to the endpoint.

    Ok, what are you waiting for? Deploy and run your app! Walk around and focus on a well-lit area on the floor. Once you have placed two points on the recognized horizontal planes, you should see a line between the two points that are just next to the end point (green mark) ... something like this:

    What we have achieved

    Good work! You have successfully learned to measure with ARKit between two points on a horizontal plane. The next time you want to measure a flat surface, use this created app instead of a tape measure.

    Let's talk about the tutorial: placing a grid over a recognized horizontal plane with the hitTest to create the start and end markers for both points, and creating a custom extension to calculate the distance between the two points and draw a line between them.

    If you need the full code for this project, you can find it in my GitHub Repo. I hope you liked this tutorial on ARKit. If you have comments or feedback, you are welcome to leave it in the comment section. Happy coding!

    Do not miss : How to measure the ground with ARKit

    Cover image & screenshots by Ambuj Punn / Next Reality

    Source link