Using Swift 5.5, iOS 14 Trying to create a simple 3D bar chart and I immediately find myself in trouble.
I wrote this code
for i in stride(from: 0, to: 6, by: 0.5) {
let rnd = CGFloat.random(in: 1.0...4.0)
let targetGeometry = SCNBox(width: 0.5,
height: rnd,
length: 0.5,
chamferRadius: 0.2)
targetGeometry.firstMaterial?.fillMode = .lines
targetGeometry.firstMaterial?.diffuse.contents = UIColor.blue
let box = SCNNode(geometry: targetGeometry)
box.simdPosition = SIMD3(x: 0, y: 0, z: 0)
coreNode.addChildNode(box)
}
This works well, but all the bars a centred around their centre. But how can I ask SceneKit to change the alignment?
Almost got this working with this code...
box.simdPosition = SIMD3(x: Float(i) - 3,
y: Float(rnd * 0.5),
z: 0)
But the result isn't right... I want/need the bar to grow from the base.
How can I make this grow from the base?
--Updated--1
Tried Andy Jazz suggestion replacing simdposition with the following formulae.
box.simdPosition = SIMD3(x:box.simdPosition.x, y:0, z:0)
box.simdPivot.columns.3.y = box.boundingBox.max.y - Float(rnd * 0.5)
--Updated--2
But although the boxes align at the base, when I change the values they still grow at both ends before realigning. Here is a video.
https://youtu.be/kVbW31ycvWc
--Updated--3
Ok, Thanks Andy this works...
var rnds:[CGFloat] = []
for i in 0..<12 {
let rnd = CGFloat.random(in: 1.0...2.0)
let targetGeometry = SCNBox(width: 0.45, height: rnd, length: 0.45, chamferRadius: 0.25)
let newNode = SCNNode(geometry: targetGeometry)
sourceNodes[i].simdPivot.columns.3.y = newNode.boundingBox.min.y
rnds.append(rnd)
}
for k in 0..<12 {
let targetGeometry = SCNBox(width: 0.45, height: rnds[k], length: 0.45, chamferRadius: 0.25)
targetGeometry.firstMaterial?.fillMode = .lines
targetGeometry.firstMaterial?.diffuse.contents = UIColor.blue
sourceNodes[k].geometry = targetGeometry
sourceNodes[k].simdPosition = SIMD3(x: sourceNodes[k].simdPosition.x, y: 0, z: 0)
}
--Update--4
Spoke too soon... animate this using a morpher and a SCNTransaction.. and it doesn't work.
var rnds:[CGFloat] = []
for i in 0..<12 {
let rnd = CGFloat.random(in: 1.0...2.0)
let targetGeometry = SCNBox(width: 0.45, height: rnd, length: 0.45, chamferRadius: 0.25)
let newNode = SCNNode(geometry: targetGeometry)
sourceNodes[i].simdPivot.columns.3.y = newNode.boundingBox.min.y
rnds.append(rnd)
}
for k in 0..<12 {
let targetGeometry = SCNBox(width: 0.45, height: rnds[k], length: 0.45, chamferRadius: 0.25)
targetGeometry.firstMaterial?.fillMode = .lines
targetGeometry.firstMaterial?.diffuse.contents = UIColor.blue
let morpher = SCNMorpher()
morpher.targets = [sourceNodes[k].geometry!,targetGeometry]
sourceNodes[k].morpher = morpher
SCNTransaction.begin()
SCNTransaction.animationDuration = 1.8
sourceNodes[k].morpher?.setWeight(1.0, forTargetAt: 1)
sourceNodes[k].simdPosition = SIMD3(x: sourceNodes[k].simdPosition.x, y: 0, z: 0)
SCNTransaction.commit()
}
--Updated--5
Tried this code to animate the change...
for k in 0..<12 {
let targetGeometry = SCNBox(width: 0.45, height: rnds[k], length: 0.45, chamferRadius: 0.25)
targetGeometry.firstMaterial?.fillMode = .lines
targetGeometry.firstMaterial?.diffuse.contents = UIColor.blue
sourceNodes[k].geometry = targetGeometry
let morpher = SCNMorpher()
morpher.targets = [sourceNodes[k].geometry!,targetGeometry]
sourceNodes[k].morpher = morpher
let animation = CABasicAnimation(keyPath: "morpher.weights")
animation.fromValue = 0.0
animation.toValue = 1.0
animation.repeatCount = Float.infinity;
animation.duration = 3.5
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
sourceNodes[k].simdPosition = SIMD3(x: sourceNodes[k].simdPosition.x, y: 0, z: 0)
}
Sadly this only half works too, so the bars are level at the base, but the animation is simply a snap to the new value.
--Updated--6
SCNAction doesn't animate the changes either
for k in 0..<12 {
let targetGeometry = SCNBox(width: 0.45, height: rnds[k], length: 0.45, chamferRadius: 0.25)
targetGeometry.firstMaterial?.fillMode = .lines
targetGeometry.firstMaterial?.diffuse.contents = UIColor.blue
sourceNodes[k].geometry = targetGeometry
let changeGeometry = SCNAction.customAction(duration: 10) { (node, elapsedTime) -> () in
sourceNodes[k].geometry = targetGeometry
sourceNodes[k].simdPosition = SIMD3(x: sourceNodes[k].simdPosition.x, y: 0, z: 0)
}
sourceNodes[k].runAction(changeGeometry)
CodePudding user response:
You have to position a pivot point of each bar to its base:
boxNode.simdPivot.columns.3.y = someFloatNumber
To reposition a pivot to bar's base use bounding box property:
boxNode.simdPivot.columns.3.y = (boxNode.boundingBox.min.y as? simd_float1)!
After pivot's offset, reposition boxNode towards negative direction of Y-axis.
boxNode.position.y = 0