场景Kit - SCNText居中不正确

10
我尝试使用以下代码使文本字符串(SCNText)适应框(SCNBox)。框的大小看起来正确,但文本不在框的正中心。有什么想法或解决方法吗?谢谢
let geoText = SCNText(string: "Hello", extrusionDepth: 1.0)
geoText.font = UIFont (name: "Arial", size: 8)
geoText.firstMaterial!.diffuse.contents = UIColor.red
let textNode = SCNNode(geometry: geoText)

let (minVec, maxVec) = textNode.boundingBox
scnScene.rootNode.addChildNode(textNode)

let w = CGFloat(maxVec.x - minVec.x)
let h = CGFloat(maxVec.y - minVec.y)
let d = CGFloat(maxVec.z - minVec.z)

let geoBox = SCNBox(width: w, height: h, length: d, chamferRadius: 0)
geoBox.firstMaterial!.diffuse.contents = UIColor.green.withAlphaComponent(0.5)
scnScene.rootNode.addChildNode(boxNode)

文本不在盒子中心

编辑:我已经添加了一个仅包含字符串(没有SCNBox节点)并使用debugOptions showBoundingBoxes调试选项来查看其边界框的新图像

输入图像描述

解决方法1:

根据vdugnist的答案,我创建了一个游乐场代码,供任何想进行测试的人使用:

import UIKit
import SceneKit
import PlaygroundSupport

var sceneView = SCNView(frame: CGRect(x: 0, y: 0, width: 600, height: 600))
var scene = SCNScene()
sceneView.scene = scene
PlaygroundPage.current.liveView = sceneView

let geoText = SCNText(string: "Hello", extrusionDepth: 1.0)
geoText.font = UIFont (name: "Arial", size: 12)
geoText.firstMaterial!.diffuse.contents = UIColor.red
let textNode = SCNNode(geometry: geoText)

let (minVec, maxVec) = textNode.boundingBox
textNode.position = SCNVector3(x: (minVec.x - maxVec.x) / 2, y: minVec.y - maxVec.y, z: 0)
textNode.pivot = SCNMatrix4MakeTranslation((maxVec.x - minVec.x) / 2, 0, 0)
scene.rootNode.addChildNode(textNode)

let w = CGFloat(maxVec.x - minVec.x)
let h = CGFloat(maxVec.y - minVec.y)
let d = CGFloat(maxVec.z - minVec.z)

let geoBox = SCNBox(width: w, height: h, length: d, chamferRadius: 0)
geoBox.firstMaterial!.diffuse.contents =   UIColor.green.withAlphaComponent(0.5)
let boxNode = SCNNode(geometry: geoBox)
boxNode.position = SCNVector3Make((maxVec.x - minVec.x) / 2 + minVec.x, (maxVec.y - minVec.y) / 2 + minVec.y, 0);
textNode.addChildNode(boxNode)

enter image description here

解决方案2:

我需要将文本移动到0,0,0位置,而不是同时移动文本和周围的框,因此我继续改变解决方案1中文本的中心点。现在代码如下所示:

import UIKit
import SceneKit
import PlaygroundSupport


var sceneView = SCNView(frame: CGRect(x: 0, y: 0, width: 600, height: 600))
var scene = SCNScene()
sceneView.scene = scene
PlaygroundPage.current.liveView = sceneView


let geoText = SCNText(string: "Hello", extrusionDepth: 1.0)
geoText.font = UIFont (name: "Arial", size: 12)
geoText.firstMaterial!.diffuse.contents = UIColor.red
let textNode = SCNNode(geometry: geoText)

let (minVec, maxVec) = textNode.boundingBox
textNode.pivot = SCNMatrix4MakeTranslation((maxVec.x - minVec.x) / 2 + minVec.x, (maxVec.y - minVec.y) / 2 + minVec.y, 0)
scene.rootNode.addChildNode(textNode)

let w = CGFloat(maxVec.x - minVec.x)
let h = CGFloat(maxVec.y - minVec.y)
let d = CGFloat(maxVec.z - minVec.z)

let geoBox = SCNBox(width: w, height: h, length: d, chamferRadius: 0)
geoBox.firstMaterial!.diffuse.contents = UIColor.green.withAlphaComponent(0.6)
let boxNode = SCNNode(geometry: geoBox)
scene.rootNode.addChildNode(boxNode)

苹果停止支持SceneKit了吗?在WWDC21上,我还没有看到任何相关的信息。 - Hope
4个回答

16

SCNText 与其他几何形状的区别在于其原点位于左下角。而在其他几何形状中,它位于底部中心。

要在父节点中固定文本位置,可以将其 pivotPoint.x 设置为宽度的一半:

SCNVector3 min, max;
[textNode getBoundingBoxMin:&min max:&max];
textNode.pivot = SCNMatrix4MakeTranslation((max.x - min.x) / 2, 0, 0);

为了修正子节点位置,您应将它们的位置设置为宽度的一半加上min值:

SCNVector3 min, max;
[textNode getBoundingBoxMin:&min max:&max];
subnode.position = SCNVector3Make((max.x - min.x) / 2 + min.x, (max.y - min.y) / 2 + min.y, 0);

谢谢,但看起来这不是一个解决方案(我已经尝试过了,但情况变得更糟了 - 或许我没有完全理解你的想法?)。从逻辑上讲,在我的问题图像中,中心点在x轴和y轴上都不在正确的位置。你的答案只尝试修复了x轴,显然这是不够的。如果你能在这里发布完整的修复代码,那就最好了。 - Tony
2
你能否尝试将盒子节点作为文本节点的子节点添加,而不是根节点,并使用我第二个解决方案中的坐标?我在你的代码中看到的问题是,你从文本节点获取boundingBox,在文本节点坐标系中返回,并在根节点坐标系中使用它们。 - vdugnist
这对我来说还不错,我认为这就是你想要的吧?textNode.pivot = SCNMatrix4MakeTranslation((max.x - min.x) / 2 + min.x, (max.y - min.y) / 2 + min.y, 0); - Jon Weinraub

10

Swift: 如何正确地将文本节点在父节点中居中对齐

let (min, max) = textNode.boundingBox

let dx = min.x + 0.5 * (max.x - min.x)
let dy = min.y + 0.5 * (max.y - min.y)
let dz = min.z + 0.5 * (max.z - min.z)
textNode.pivot = SCNMatrix4MakeTranslation(dx, dy, dz)

let textNodeParent = SCNNode()
textNodeParent.addChildNode(textNode)

0
我认为你可能会受到 SCNText 对原点的不寻常处理的影响。如果我没记错,Y 轴零点在字符串基线上,而不是下降部分的底部。
debugOptions 中打开 showBoundingBoxes ,看看是否有帮助。

我已经打开了所有的调试选项并进行了检查。一切看起来都很好。因此,我不知道问题出在哪里。也许这是一个API的bug(因为代码非常简单,逻辑也很清晰)!? - Tony
这很可能不是API的错误。但既然它没有按照您的期望工作,它确实可能是文档错误。我们能看到打开边界框的屏幕截图吗? - Hal Mueller
我无法在此处添加图片(在此评论中)。因此,我已经编辑了我的问题以添加该图片。无论如何,更多的信息总是更好的。 - Tony

0
要找到边界框的中心,您需要在除以2之前将最小值和最大值相加(而不是相减)。
textNode.position = SCNVector3(x: (minVec.x + maxVec.x) / 2, y: minVec.y + maxVec.y, z: 0)

抱歉,但这不是正确的答案。我将点(0, 0, 0)作为中心,类似于盒子,不会像您建议的那样将文本移动得太远。 - Tony

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接