Tony
Tony

Reputation: 1611

SceneKit - SCNText centering incorrectly

I have tried to make a text string (SCNText) fit inside a box (SCNBox) in the code below. The size of the box looks correct but the text is not in the right center of the box. Any idea or solution? Thanks

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)

The text is not the center of the box

Edited: I have added a new image of the string only (no SCNBox node) with debugOptions showBoundingBoxes to see its bounding box

enter image description here

Solution 1:

From vdugnist's answer, I create a playground code for anyone who wants to test:

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

Solution 2:

I need to move the text to position zero (0, 0, 0) instead of moving both text and the around box, thus I continue to change the pivot of the text from Solution 1. Now the code is as the following:

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)

Upvotes: 10

Views: 4906

Answers (4)

Peter Kreinz
Peter Kreinz

Reputation: 8628

Swift: center text-node in parent-node correctly

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)

Upvotes: 11

vdugnist
vdugnist

Reputation: 381

The difference of SCNText from other geometries is that SCNText origin point positioned at bottom left corner. In other geometries, it is a bottom center.

To fix text position in parent node you can set its pivotPoint.x to half of width:

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

To fix subnodes position, you should set their position to half of width plus 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);

Upvotes: 17

Hal Mueller
Hal Mueller

Reputation: 7646

I think you might be getting bitten by SCNText's unusual treatment of origin. IIRC the Y zero is at the string's baseline, not at the bottom of the descenders.

Turn on showBoundingBoxes In the debugOptions and see if that helps.

Upvotes: 0

mnuages
mnuages

Reputation: 13462

To find the center of the bounding box you need to add (not subtract) the min and max before you divide by 2.

textNode.position = SCNVector3(x: (minVec.x + maxVec.x) / 2, y: minVec.y + maxVec.y, z: 0)

Upvotes: 0

Related Questions