如何使用ARKit计算从点2到点3以及3到4的距离?

时间:2019-04-24 16:47:27

标签: ios swift arkit

我正在制作一个用于计算距离和面积的应用程序,问题是我制作了一个数组,并在其中附加了节点。

func calculate () {
    let start = dotNodes[0]
    let end = dotNodes[1]

    let a = end.position.x - start.position.x
    let b = end.position.y - start.position.y
    let c = end.position.z - start.position.z

    let distance =  sqrt(pow(a,2) + pow(b,2) + pow(c, 2))

    updateText(text:"\(abs( distance))", atPosition: (end.position))
}

现在,起点是0索引,终点是1索引,但这仅是两个点。如何计算从2到3以及从3到4的距离,依此类推,最后一点到达最后一点时,应该给我面积?

2 个答案:

答案 0 :(得分:1)

效果最好(也是最简单)的方法是使用SIMD- https://developer.apple.com/documentation/accelerate/simd/working_with_vectors

let dist = simd_distance(start, end)

其中向量可能应重新定义为simd_float3(如果使用的是Swift 5,则应重新定义为SIMD3<Float>)。

P.S。您需要先导入simd框架。

答案 1 :(得分:1)

就像@Maxim所说的那样,您可以从简化计算开始^ ______ ^。

但是,我将尝试使用GLK数学帮助器方法回答您的问题,如果您有兴趣,可以在这里阅读更多信息:GLK Documentation

本质上,您需要做的是遍历位置数组,并以2的段为单位计算它们之间的距离。当您的最后一次迭代只有一个元素时,您将计算该位置与第一个元素之间的位置。

由于我不是数学高手,因此我在StackOverflow上进行了快速搜索以找到解决方案,并利用了@Gasim在Iterate Over Collections Two At A Time In Swift帖子中提供的答案。

由于我的尝试相当冗长,因此我没有逐步介绍每个步骤,而是提供了完整评论的答案,希望能为您指明正确的方向。

一如既往,如果其他人可以帮助重构和/或改进代码,请放心:

//
//  ViewController.swift
//  Measuring Example
//
//  Created By Josh Robbins (∩`-´)⊃━☆゚.*・。゚* on 27/04/2019.
//  Copyright © 2019 BlackMirrorz. All rights reserved.
//

import UIKit
import ARKit

class ViewController: UIViewController {

  @IBOutlet weak var augmentedRealityView: ARSCNView!
  var augmentedRealityConfiguration = ARWorldTrackingConfiguration()
  var augmentedRealitySession = ARSession()

  var markerNodes = [SCNNode]()
  typealias NodeNameData = (name: String, node: SCNNode)
  typealias DistanceData = (distance: Float, positionA: GLKVector3, positionB: GLKVector3)

  //---------------------
  //MARK:- Initialization
  //---------------------

  override func viewDidLoad() {

    super.viewDidLoad()
    setupARSession()

  }

  /// Sets Up Our ARSession
  func setupARSession(){

    augmentedRealityView.session = augmentedRealitySession
    augmentedRealitySession.run(augmentedRealityConfiguration, options: [.removeExistingAnchors, .resetTracking])

  }

  /// Creates A Node To Mark The Touch Position In The Scene
  ///
  /// - Returns: SCNNode
  func markerNode() -> SCNNode{

    let node = SCNNode(geometry: SCNSphere(radius: 0.01))
    node.geometry?.firstMaterial?.diffuse.contents = UIColor.cyan
    return node

  }

  //------------------------
  //MARK:- Marker Placemenet
  //------------------------

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

    //1. Get The Users Current Touch Point & Check We Have A Valid HitTest Result
    guard let touchPoint = touches.first?.location(in: self.augmentedRealityView),
          let hitTest = self.augmentedRealityView.hitTest(touchPoint, types: .featurePoint).first
    else { return }

    //2. Get The World Transorm & Create An SCNNode At The Converted Touch Position
    let transform = hitTest.worldTransform
    let node = markerNode()
    node.position = SCNVector3(transform.columns.3.x, transform.columns.3.y, transform.columns.3.z)
    self.augmentedRealityView.scene.rootNode.addChildNode(node)

    //3. Add The Node To Our Markers Array So We Can Calculate The Distance Later
    markerNodes.append(node)

    //4. If We Have 5 Marker Nodes Then Calculate The Distances Between Them & Join Them Together
    if markerNodes.count == 5{
      calculateMarkerNodeDistances()
      markerNodes.removeAll()
    }

  }

  //-------------------
  //MARK:- Calculations
  //-------------------

  /// Enemurates Our Marker Nodes & Creates A Joining Node Between Them
  func calculateMarkerNodeDistances(){

    var index = 0;

    while index < markerNodes.count {

      let nodeA = markerNodes[index];
      var nodeB : SCNNode? = nil;

      if index + 1 < markerNodes.count {
         nodeB = markerNodes[index+1];
      }

      //1. Create A Joining Node Between The Two Nodes And Calculate The Distance
      if let lastNode = nodeB{
        let nodeA = NodeNameData("Node \(index)", nodeA)
        let nodeB = NodeNameData("Node \(index+1)", lastNode)
        self.augmentedRealityView.scene.rootNode.addChildNode(joiningNode(between: [nodeA, nodeB]))

      }else{

        //2. Here We Can Assume The We Have Reached The Last Node So We Calculate The Distance Between The 1st & Last Nodes
        guard let initialNode = markerNodes.first, let lastNode = markerNodes.last else { return }
        let nodeA = NodeNameData("Node 0 ", initialNode)
        let nodeB = NodeNameData("Node \(markerNodes.count)", lastNode)
        self.augmentedRealityView.scene.rootNode.addChildNode(joiningNode(between: [nodeA, nodeB]))
      }

      //Increment By 1 So We Join The Nodes Together In The Correct Sequence e.g. (1, 2), (3, 4) And Not (1, 2), (3, 4)
      index += 1;
    }

  }


  /// Creates A Joining Node Between Two Names
  ///
  /// - Parameter nodes: [NodeNameData]
  /// - Returns: MeasuringLineNode
  func joiningNode(between nodes: [NodeNameData]) -> MeasuringLineNode{

    let distance = calculateDistanceBetweenNodes([nodes[0], nodes[1]])
    let joiner = MeasuringLineNode(startingVector: distance.positionA, endingVector: distance.positionB)
    return joiner

  }

  /// Calculates The Distance Between Two SCNNodes
  ///
  /// - Parameter nodes: [NodeNameData]
  /// - Returns: DistanceData
  func calculateDistanceBetweenNodes(_ nodes: [NodeNameData]) -> DistanceData{

    //1. Calculate The Distance
    let positionA = GLKVectorThreeFrom(nodes[0].node.position)
    let positionB = GLKVectorThreeFrom(nodes[1].node.position)
    let distance = GLKVector3Distance(positionA, positionB)
    let meters = Measurement(value: Double(distance), unit: UnitLength.meters)
    print("Distance Between Markers [ \(nodes[0].name) & \(nodes[1].name) ]  = \(String(format: "%.2f", meters.value))m")

    //2. Return The Distance A Positions Of The Nodes
    return (distance, positionA, positionB)

  }

  /// Creates A GLKVector3 From An SCNVectore3
  ///
  /// - Parameter vector3: SCNVector3
  /// - Returns: GLKVector3
  func GLKVectorThreeFrom(_ vector3: SCNVector3) -> GLKVector3 { return GLKVector3Make(vector3.x, vector3.y, vector3.z) }

}

//-------------------------
//MARK:- Mesuring Line Node
//-------------------------

class MeasuringLineNode: SCNNode{

  /// Creates A Line Between Two SCNNodes
  ///
  /// - Parameters:
  ///   - vectorA: GLKVector3
  ///   - vectorB: GLKVector3
  init(startingVector vectorA: GLKVector3, endingVector vectorB: GLKVector3) {

    super.init()

    let height = CGFloat(GLKVector3Distance(vectorA, vectorB))

    self.position = SCNVector3(vectorA.x, vectorA.y, vectorA.z)

    let nodeVectorTwo = SCNNode()
    nodeVectorTwo.position = SCNVector3(vectorB.x, vectorB.y, vectorB.z)

    let nodeZAlign = SCNNode()
    nodeZAlign.eulerAngles.x = Float.pi/2

    let box = SCNBox(width: 0.001, height: height, length: 0.001, chamferRadius: 0)
    let material = SCNMaterial()
    material.diffuse.contents = UIColor.white
    box.materials = [material]

    let nodeLine = SCNNode(geometry: box)
    nodeLine.position.y = Float(-height/2)
    nodeZAlign.addChildNode(nodeLine)

    self.addChildNode(nodeZAlign)

    self.constraints = [SCNLookAtConstraint(target: nodeVectorTwo)]
  }

  required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }

}

基于这个简单(希望准确的答案),结果是这样的:

Distance Between Markers [ Node 0 & Node 1 ]  = 0.14m
Distance Between Markers [ Node 1 & Node 2 ]  = 0.09m
Distance Between Markers [ Node 2 & Node 3 ]  = 0.09m
Distance Between Markers [ Node 3 & Node 4 ]  = 0.05m
Distance Between Markers [ Node 0  & Node 5 ]  = 0.36m

在我的示例中,我正在计算五个节点的距离,但是您可以将其称为任意点。当然,您将需要使用公式来计算面积本身。但是,这足以使您指向正确的方向。

希望有帮助...