user3069232
user3069232

Reputation: 8995

Align a node with its neighbor in SceneKit

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...

var virgin = true

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?

enter image description here

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.

https://youtu.be/KJgvdBFBfyc

How can I make this grow from the base?

--Updated with working solution--

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)

Worked well, to which I added some animation! Thanks Andy.

 changer = changeling.sink(receiveValue: { [self] _ in
 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.01)
   let newNode = SCNNode(geometry: targetGeometry)
                
    sourceNodes[i].simdPivot.columns.3.y = newNode.boundingBox.min.y
    sourceNodes[i].simdPosition = SIMD3(x: sourceNodes[i].simdPosition.x, y: 0, z: 0)
     rnds.append(rnd)
   }
              
            
for k in 0..<12 {
  if virgin {
    coreNode.addChildNode(sourceNodes[k])
  }
  let targetGeometry = SCNBox(width: 0.45, height: rnds[k], length: 0.45, chamferRadius: 0.01)
  targetGeometry.firstMaterial?.fillMode = .lines
  targetGeometry.firstMaterial?.diffuse.contents = UIColor.blue
                            
  let morpher = SCNMorpher()
                            
  morpher.targets = [targetGeometry]
  sourceNodes[k].morpher = morpher
                            
  let animation = CABasicAnimation(keyPath: "morpher.weights[0]")
  animation.toValue = 1.0
  animation.repeatCount = 0.0
  animation.duration = 1.0
  animation.fillMode = .forwards
  animation.isRemovedOnCompletion = false                     
  sourceNodes[k].addAnimation(animation, forKey: nil)
}          
virgin = false
})

Upvotes: 1

Views: 110

Answers (1)

Andy Jazz
Andy Jazz

Reputation: 58093

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

Upvotes: 1

Related Questions