10

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)
aheze
  • 24,434
  • 8
  • 68
  • 125
Tony
  • 1,551
  • 20
  • 21
  • Did Apple stoped SceneKit support ? At WWDC21 I did not see anything about it so far. – Hope Jun 10 '21 at 09:03

4 Answers4

16

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);
vdugnist
  • 371
  • 2
  • 7
  • Thanks, but look like it is not a solution (I have tried but it is worse - perhaps I don't get all your ideas?). Logically, in my question image, the central point is not in the right position for both axes x and y. Your answer has just tried to fix x only, clearly it is not enough. It is the best if you post a full fix code here. – Tony Sep 04 '17 at 01:32
  • 2
    Can you try to add box node as a child of text node instead of a root node, using coordinates from my second solution? The problem that I see in your code is that you're getting boundingBox from textNode, which returned in textNode coordinate system, and using them in rootNode coordinate system. – vdugnist Sep 04 '17 at 11:10
  • This works for me decently I think this is what you want? `textNode.pivot = SCNMatrix4MakeTranslation((max.x - min.x) / 2 + min.x, (max.y - min.y) / 2 + min.y, 0);` – Jon Weinraub Mar 29 '20 at 21:24
10

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)
Peter Kreinz
  • 7,979
  • 1
  • 64
  • 49
0

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)
mnuages
  • 13,049
  • 2
  • 23
  • 40
  • Sorry, but it is not the right answer. I take the point(0, 0, 0) as the center, similar to the box, not to move the text far aways as your suggestion. – Tony Jul 18 '17 at 23:16
0

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.

Hal Mueller
  • 7,019
  • 2
  • 24
  • 42
  • I have turned on all debug options and checked already. All look good. Thus I have no clue. Perhaps it is an API bug (since the code is so simple and the logic is quite clear)!? – Tony Jul 19 '17 at 06:31
  • It's unlikely to be an API bug. But since it isn't working the way you expect, it legitimately could be a documentation bug. Can we see a screenshot with bounding boxes turned on? – Hal Mueller Jul 19 '17 at 06:34
  • I cannot add the image here (in this comment). Thus I have edited my question to add that image. More info is better anyways – Tony Jul 19 '17 at 07:02