美文网首页
ARKit连载三之太空尺

ARKit连载三之太空尺

作者: 阿文灬 | 来源:发表于2017-09-09 16:10 被阅读0次
    太空尺.gif

    太空尺原理非常简单:通过ARKit捕捉真实世界的两个点,然后计算出两个点之间的距离。所以可以直接下载代码查看,下面列出重要实现步骤及代码。
    1, 是否划线-状态控制

    class ViewController
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            if !isMeasuring {
                // 开始测量
                isMeasuring = true
                targetImageView.image = UIImage(named: "GreenTarget")
                
                vectorStart = SCNVector3()
                vectorEnd = SCNVector3()
            } else {
                // 测量结束
                isMeasuring = false
                
                if let line = currentLine {
                    lines.append(line)
                    currentLine = nil
                    targetImageView.image = UIImage(named: "WhiteTarget")
                }
            }
        }
    

    2, 因为我们要时刻扫描划线,所以实现代理方法:func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval)

    class ViewController
        func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
            DispatchQueue.main.async {
                scanWorld()
            }
        }
    

    后边所有步骤及代码几乎都在scanWorld方法内为其服务

    3, 这里捕捉点,就通过屏幕的中心点捕捉吧!

    class ViewController.scanWorld()
            let point = view.center
            let results = sceneView.hitTest(point, types: [.featurePoint])
            guard let result = results.first else {
                return
            }
            let transform = result.worldTransform
            let worldPosition = SCNVector3Make(transform.columns.3.x, transform.columns.3.y, transform.columns.3.z)
    

    4, 控制划线状态与划线的逻辑:

    • 在刚开启划线状态时,通过步骤3中扫描的点作为线的终点,如果起点都还没有确定,那么该点还是线的起点。
    • 在确定起点时,创建Line对象
    • 在确定终点时,更新线。因为划线状态没有被切换时,终点一直在变,也就是前面创建的展示线的节点一直在变
    class ViewController.scanWorld()
            if isMeasuring {
                // 确定线的起点,创建线条
                if vectorStart == vectorZero {
                    vectorStart = worldPosition
                    currentLine = Line(sceneView: sceneView, startVector: vectorStart, unit: unit)
                }
                
                // 更新线条
                currentLine?.update(to: worldPosition)
                infoLabel.text = currentLine?.distance ?? "update line..."
            }
    
    extension SCNVector3: Equatable {
        
        public static func == (lhs: SCNVector3, rhs: SCNVector3) -> Bool {
            return (lhs.x == rhs.x) && (lhs.y == rhs.y) && (lhs.z == rhs.z)
        }
    }
    

    5, 上边代码创建Line不是关键,而是Line的更新:currentLine?.update(to: worldPosition)

    • 此时worldPosition就表示currentLine的终点
    • 由于线的终点变化,所以需要移除掉之前的线节点
    • 通过起点与新的终点,创建并展示新的线节点
    class Line:
        var distance: String? {
            let distanceX = startVector.x - endVector.x
            let distanceY = startVector.y - endVector.y
            let distanceZ = startVector.z - endVector.z
            let value = sqrt((distanceX * distanceX) + (distanceY * distanceY) + (distanceZ * distanceZ))
            
            return String(format:"%0.2f %@", value*unit.factor, unit.name)
        }
    
        func update(to vector: SCNVector3) {
            lineNode?.removeFromParentNode()
            
            endVector = vector
            
            lineNode = startVector.line(to: endVector, color: color)
            sceneView.scene.rootNode.addChildNode(lineNode!)
            
            // 文字节点
            text.string = distance // text是SCNText,是文字节点的几何形
            textNode.position = SCNVector3((startVector.x + vector.x) / 2.0 , (startVector.y + vector.y) / 2.0 ,(startVector.z + vector.z) / 2.0 )
            
            endNode.position = vector
            if endNode.parent == nil {
                sceneView.scene.rootNode.addChildNode(endNode)
            }
        }
    
    

    6, 通过两点之间在3D场景划线:startVector.line(to: endVector, color: color)

    extension SCNVector3
        func line(to vector: SCNVector3, color: UIColor) -> SCNNode {
            let indices: [UInt32] = [0, 1]
            
            let source = SCNGeometrySource(vertices: [self, vector])
            let element = SCNGeometryElement(indices: indices, primitiveType: .line)
            
            let geometry = SCNGeometry(sources: [source], elements: [element])
            geometry.firstMaterial?.diffuse.contents = color
            
            let node = SCNNode(geometry: geometry)
            return node
        }
    

    相关文章

      网友评论

          本文标题:ARKit连载三之太空尺

          本文链接:https://www.haomeiwen.com/subject/irxnjxtx.html