草庐IT

swift - arkit anchor 或节点在相机中可见,位于截锥体的左侧或右侧

coder 2023-09-05 原文

我如何检测 ARAnchor 当前是否在相机中可见,我需要在相机 View 发生变化时进行测试。 当不在屏幕上时,我想在屏幕边缘放置指向 anchor 方向的箭头。我需要知道节点是位于视锥体的左侧还是右侧。

我现在正在这样做,但它说 pin 是可见的,而实际上它不可见,而且 X 值似乎不正确?也许渲染器截锥体与屏幕相机不匹配?

 var deltaTime = TimeInterval()

 public func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        deltaTime = time - lastUpdateTime
        if deltaTime>1{

            if let annotation = annotationsByNode.first {

                let node = annotation.key.childNodes[0]

                if !renderer.isNode(node, insideFrustumOf: renderer.pointOfView!)
                {
                    print("Pin is not visible");
                }else {
                    print("Pin is visible");
                }
                let pnt = renderer.projectPoint(node.position)

                print("pos ", pnt.x, " ", renderer.pointOfView!.position)


            }
            lastUpdateTime = time
        }

    }

更新:代码用于显示节点是否可见,我如何判断节点相对于相机平截头体是向左还是向右?

更新2!正如 Bhanu Birani 建议的答案

let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height
let leftPoint = CGPoint(x: 0, y: screenHeight/2)
let rightPoint = CGPoint(x: screenWidth,y: screenHeight/2)

let leftWorldPos = renderer.unprojectPoint(SCNVector3(leftPoint.x,leftPoint.y,0))
let rightWorldPos = renderer.unprojectPoint(SCNVector3(rightPoint.x,rightPoint.y,0))
let distanceLeft = node.position - leftWorldPos
let distanceRight = node.position - rightWorldPos
let dir = (isVisible) ? "visible" : ( (distanceLeft.x<distanceRight.x) ? "left" : "right")

我终于让它工作了,它使用了屏幕左右两侧的 Bhanu Birani 的想法,但我得到了不同的世界位置,unProjectPoint 并且还得到了距离的标量值,我比较它以获得左/右方向.也许有更好的方法,但它对我有用

public func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        deltaTime = time - lastUpdateTime
        if deltaTime>0.25{

            if let annotation = annotationsByNode.first {
                guard let pointOfView = renderer.pointOfView else {return}
                let node = annotation.key.childNodes[0]
                let isVisible = renderer.isNode(node, insideFrustumOf: pointOfView)

                let screenWidth = UIScreen.main.bounds.width
                let screenHeight = UIScreen.main.bounds.height
                let leftPoint = CGPoint(x: 0, y: screenHeight/2)
                let rightPoint = CGPoint(x: screenWidth,y: screenHeight/2)

                let leftWorldPos = renderer.unprojectPoint(SCNVector3(leftPoint.x, leftPoint.y,0))
                let rightWorldPos =  renderer.unprojectPoint(SCNVector3(rightPoint.x, rightPoint.y,0))
                let distanceLeft = node.worldPosition.distance(vector: leftWorldPos)
                let distanceRight = node.worldPosition.distance(vector: rightWorldPos)

                //let pnt = renderer.projectPoint(node.worldPosition)
                //guard let pnt = renderer.pointOfView!.convertPosition(node.position, to: nil) else {return}

                let dir = (isVisible) ? "visible" : ( (distanceLeft<distanceRight) ? "left" : "right")
                print("dir" , dir, " ", leftWorldPos , " ", rightWorldPos)
                lastDir=dir
                delegate?.nodePosition?(node:node, pos: dir)
            }else {
               delegate?.nodePosition?(node:nil, pos: lastDir )
            }
            lastUpdateTime = time
        }

extension SCNVector3
{

    /**
     * Returns the length (magnitude) of the vector described by the SCNVector3
     */
    func length() -> Float {
        return sqrtf(x*x + y*y + z*z)
    }

    /**
     * Calculates the distance between two SCNVector3. Pythagoras!
     */
    func distance(vector: SCNVector3) -> Float {
        return (self - vector).length()
    }


}

最佳答案

从以下屏幕位置转换光线:

  • leftPoint = CGPoint(0, screenHeight/2)(屏幕左中)
  • rightPoint = CGPoint(screenWidth, screenHeight/2)(屏幕右中)

将 CGPoint 转换为世界位置:

  • leftWorldPos = convertCGPointToWorldPosition(leftPoint)
  • rightWorldPos = convertCGPointToWorldPosition(rightPoint)

计算节点到两个世界位置的距离:

  • distanceLeft = node.position - leftWorldPos
  • distanceRight = node.position - rightWorldPos

比较距离以找到到节点的最短距离。用最短距离向量为物体定位方向箭头。

这是来自 tsukimi 的代码,用于检查对象是在屏幕的右侧还是左侧:

public func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        deltaTime = time - lastUpdateTime
        if deltaTime>0.25{

            if let annotation = annotationsByNode.first {
                guard let pointOfView = renderer.pointOfView else {return}
                let node = annotation.key.childNodes[0]
                let isVisible = renderer.isNode(node, insideFrustumOf: pointOfView)

                let screenWidth = UIScreen.main.bounds.width
                let screenHeight = UIScreen.main.bounds.height
                let leftPoint = CGPoint(x: 0, y: screenHeight/2)
                let rightPoint = CGPoint(x: screenWidth,y: screenHeight/2)

                let leftWorldPos = renderer.unprojectPoint(SCNVector3(leftPoint.x, leftPoint.y,0))
                let rightWorldPos =  renderer.unprojectPoint(SCNVector3(rightPoint.x, rightPoint.y,0))
                let distanceLeft = node.worldPosition.distance(vector: leftWorldPos)
                let distanceRight = node.worldPosition.distance(vector: rightWorldPos)

                //let pnt = renderer.projectPoint(node.worldPosition)
                //guard let pnt = renderer.pointOfView!.convertPosition(node.position, to: nil) else {return}

                let dir = (isVisible) ? "visible" : ( (distanceLeft<distanceRight) ? "left" : "right")
                print("dir" , dir, " ", leftWorldPos , " ", rightWorldPos)
                lastDir=dir
                delegate?.nodePosition?(node:node, pos: dir)
            }else {
               delegate?.nodePosition?(node:nil, pos: lastDir )
            }
            lastUpdateTime = time
        }

下面是帮助对vector进行操作的类

extension SCNVector3 {

    init(_ vec: vector_float3) {
        self.x = vec.x
        self.y = vec.y
        self.z = vec.z
    }

    func length() -> Float {
        return sqrtf(x * x + y * y + z * z)
    }

    mutating func setLength(_ length: Float) {
        self.normalize()
        self *= length
    }

    mutating func setMaximumLength(_ maxLength: Float) {
        if self.length() <= maxLength {
            return
        } else {
            self.normalize()
            self *= maxLength
        }
    }

    mutating func normalize() {
        self = self.normalized()
    }

    func normalized() -> SCNVector3 {
        if self.length() == 0 {
            return self
        }

        return self / self.length()
    }

    static func positionFromTransform(_ transform: matrix_float4x4) -> SCNVector3 {
        return SCNVector3Make(transform.columns.3.x, transform.columns.3.y, transform.columns.3.z)
    }

    func friendlyString() -> String {
        return "(\(String(format: "%.2f", x)), \(String(format: "%.2f", y)), \(String(format: "%.2f", z)))"
    }

    func dot(_ vec: SCNVector3) -> Float {
        return (self.x * vec.x) + (self.y * vec.y) + (self.z * vec.z)
    }

    func cross(_ vec: SCNVector3) -> SCNVector3 {
        return SCNVector3(self.y * vec.z - self.z * vec.y, self.z * vec.x - self.x * vec.z, self.x * vec.y - self.y * vec.x)
    }
}




extension SCNVector3{
    func distance(receiver:SCNVector3) -> Float{
        let xd = receiver.x - self.x
        let yd = receiver.y - self.y
        let zd = receiver.z - self.z
        let distance = Float(sqrt(xd * xd + yd * yd + zd * zd))

        if (distance < 0){
            return (distance * -1)
        } else {
            return (distance)
        }
    }
}

这里是将点击位置或任何 CGPoint 转换为世界转换的代码片段。

@objc func handleTap(_ sender: UITapGestureRecognizer) {

    // Take the screen space tap coordinates and pass them to the hitTest method on the ARSCNView instance
    let tapPoint = sender.location(in: sceneView)
    let result = sceneView.hitTest(tapPoint, types: ARHitTestResult.ResultType.existingPlaneUsingExtent)

    // If the intersection ray passes through any plane geometry they will be returned, with the planes
    // ordered by distance from the camera
    if (result.count > 0) {

        // If there are multiple hits, just pick the closest plane
        if let hitResult = result.first {
            let finalPosition = SCNVector3Make(hitResult.worldTransform.columns.3.x + insertionXOffset,
                                                       hitResult.worldTransform.columns.3.y + insertionYOffset,
                                                       hitResult.worldTransform.columns.3.z + insertionZOffset
                    );
        }
    }
}

以下是在没有找到平面的情况下获取 HitTest 结果的代码。

// check what nodes are tapped
let p = gestureRecognize.location(in: scnView)
let hitResults = scnView.hitTest(p, options: [:])
// check that we clicked on at least one object
if hitResults.count > 0 {
    // retrieved the first clicked object
    let result = hitResults[0] 
}

关于swift - arkit anchor 或节点在相机中可见,位于截锥体的左侧或右侧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46539121/

有关swift - arkit anchor 或节点在相机中可见,位于截锥体的左侧或右侧的更多相关文章

  1. ruby - 即时确定方法的可见性 - 2

    我正在编写一个方法,它将在一个类中定义一个实例方法;类似于attr_accessor:classFoocustom_method(:foo)end我通过将custom_method函数添加到Module模块并使用define_method定义方法来实现它,效果很好。但我无法弄清楚如何考虑类(class)的可见性属性。例如,在下面的类中classFoocustom_method(:foo)privatecustom_method(:bar)end第一个生成的方法(foo)必须是公共(public)的,第二个(bar)必须是私有(private)的。我怎么做?或者,如何找到调用我的cust

  2. [工业相机] 分辨率、精度和公差之间的关系 - 2

    📢博客主页:https://blog.csdn.net/weixin_43197380📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由Loewen丶原创,首发于CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览:一.分辨率(Resolution)1、工业相机的分辨率是如何定义的?2、工业相机的分辨率是如何选择的?二.精度(Accuracy)1、像素精度(PixelAccuracy)2、定位精度和重复定位精度(RepeatPrecision)三.公差(Tolerance)四.课后作业(Post-ClassExercises)视觉行业的初学者,甚至是做了1~2年

  3. ruby-on-rails - 如何解析位于 Amazon S3 存储桶中的 CSV 文件 - 2

    下面是我用来从应用程序中解析CSV的代码,但我想解析位于AmazonS3存储桶中的文件。当推送到Heroku时它也需要工作。namespace:csvimportdodesc"ImportCSVDatatoInventory."task:wiwt=>:environmentdorequire'csv'csv_file_path=Rails.root.join('public','wiwt.csv.txt')CSV.foreach(csv_file_path)do|row|p=Wiwt.create!({:user_id=>row[0],:date_worn=>row[1],:inven

  4. ruby - 在 ruby​​ 中对字符串进行排序以使空字符串位于末尾的好方法是什么? - 2

    在Ruby中,默认排序将空字符串放在第一位。['','g','z','a','r','u','','n'].sort给予:["","","a","g","n","r","u","z"]但是,在end处需要空字符串是很常见的。做类似的事情:['','g','z','a','r','u','','n'].sort{|a,b|a[0]&&b[0]?ab:a[0]?-1:b[0]?1:0}工作并给予:["a","g","n","r","u","z","",""]但是,这不是很可读,也不是很灵活。在Ruby中是否有一种合理且干净的方法让sort将空字符串放在最后?只映射到一个没有空字符串的数组,

  5. 区块链入门教程(6)--WeBASE-Front节点前置服务安装 - 2

    文章目录1.任务背景2.任务目标3.相关知识点4.任务实操4.1安装配置JDK4.2启动FISCOBCOS4.3下载解压WeBASE-Front4.4拷贝sdk证书文件4.5启动节点4.6访问节点4.7检查运行状态5.任务总结1.任务背景FISCOBCOS其实是有控制台管理工具,用来对区块链系统进行各种管理操作。但是对于初学者来说,还是可视化界面更友好,本节就来介绍WeBASE管理平台,这是一款微众银行开源的自研区块链中间件平台,可以降低区块链使用的门槛,大幅提高区块链应用的开发效率。微众银行是腾讯牵头设立的民营银行,在国内民营银行里还是比较出名的。微众银行参与FISCOBCOS生态建设,一定

  6. ruby - 选择包含子节点内文本的父节点 - 2

    基本上我想选择一个节点(div),其中它的子节点(h1,b,h3)包含指定的文本。Childtext1Childtext2...Childtext3我期待的是/html/div/而不是/html/div/h1我在下面有这个,但不幸的是返回了child,而不是div的xpath。expression="//div[contains(text(),'Childtext1')]"doc.xpath(expression)我期待的是/html/div/而不是/html/div/h1那么有没有一种方法可以简单地使用xpath语法来做到这一点? 最佳答案

  7. ruby - 是否可以动态检查方法可见性范围(私有(private)/公共(public)/ protected )? - 2

    如thisanswer中所述,在Ruby2.1或更高版本中,此代码:classSimpleTestprivatedefine_method:foodo42endend将定义foo作为SimpleTest的私有(private)方法实例。(在Ruby2.0和更早版本中,它不会是私有(private)的。)但是,我希望做一些不那么琐碎的事情。我想定义一个类可以扩展的DSL,并希望DSL在内部定义的方法尊重调用上下文的私有(private)/protected可见性。这可能不是很清楚,所以这里有一个例子:moduleDsldefhas_a(name)define_methodnamedo42

  8. ruby - 删除指定节点之后的所有节点 - 2

    这个问题在这里已经有了答案:Nokogiri:SelectcontentbetweenelementAandB(3个答案)关闭2年前。我正在从url中抓取文本的div,并想删除具有backtotop类的段落下方的所有内容。我在stackoverflow上看到了一段遍历代码片段,看起来很有希望,但我不知道如何将它合并,所以@el只包含第一个p.backtotop之前的所有内容分区我的代码:@doc=Nokogiri::HTML(open(url))@el=@doc.css("div")[0]end遍历片段:doc=Nokogiri::HTML(code)stop_node=doc.css

  9. ruby - 使用 Foreman 启动位于不同目录的 Rack App - 2

    我有一个成功运行多个进程的Procfile设置:#/Procfileredis:bundleexecredis-serversidekiq:bundleexecsidekiq-v-C./config.ymlforward:forward4567mock-api我需要再添加一个进程-一个位于我机器上不同目录中的Sinatra应用程序。如果我cd到该目录,我可以从终端启动它:$rackup-p4567我可以使用终端从不同的目录启动它:$sh-c'cd/Path/to/project/&&execrackup-p4567'但是我应该如何使用工头来做到这一点。我尝试添加以下内容,但它无声地失败

  10. ruby - 如何从 Chef 说明书中的库访问当前节点? - 2

    我正在尝试为ChefRecipe编写一个库,以简化一些常见的搜索。例如,我希望能够在cookbook/libraries/library.rb中执行类似的操作,然后从同一Recipe中的Recipe中使用它:moduleExampledefself.search_attribute(attribute_name)returnsearch(:nodes,node[attribute_name])endend问题是,在Chef库文件中,node对象或search函数都不可用。似乎可以使用Chef::Search::Query.new().search(...)进行搜索,但我找不到任何可以访

随机推荐