OK, took a while. Here is a greatly reduced bit of code that should, but does not show my nodes.
| |
| |
| |
| |
| |
| import SwiftUI |
| import SceneKit |
| struct ContentView: View { |
| @State private var fs_cv = "2" |
| private let scene = makeAScene() |
| private struct box: Identifiable { |
| let radius: Int |
| let position: [simd_float3] |
| var id: String { String(radius) } |
| } |
| private let boxFlake:[box] = [ |
| box(radius: 0, position: [ simd_float3(0, 0, 0)]), |
| box(radius: 1, position: [ simd_float3(0, 1, 0), |
| simd_float3(0, -1, 0), |
| simd_float3(1, 0, 0), |
| simd_float3(-1, 0, 0) ]), |
| box(radius: 2, position: [ simd_float3(0, 2, 0), |
| simd_float3(0, -2, 0), |
| simd_float3(2, 0, 0), |
| simd_float3(-2, 0, 0) ]), |
| box(radius: 3, position: [ simd_float3(3, 3, 0), |
| simd_float3(3, -3, 0), |
| simd_float3(-3, 3, 0), |
| simd_float3(-3, -3, 0) ]) |
| ] |
| var body: some View { |
| ZStack(alignment: .topTrailing) { |
| SceneView(scene: scene, options: [.autoenablesDefaultLighting, |
| .allowsCameraControl, .rendersContinuously]) |
| Picker("" |
| , selection: self.$fs_cv) { |
| let boxFlakeList: [String] = ["0","1","2","3"] |
| ForEach( boxFlakeList, id: \.self ) { Num in Text(Num) } |
| }.fixedSize() |
| .onChange(of: fs_cv, { |
| let selected_boxFlake:[box] = boxFlake.dropLast( 3 - (fs_cv as |
| NSString).integerValue ) |
| redraw_animate_scene(boxFlake: selected_boxFlake) |
| } |
| ) |
| } |
| } |
| private func redraw_animate_scene(boxFlake: [box]) { |
| for box in boxFlake { |
| for position in box.position { |
| position.pNode(drawIntoNode: scene.rootNode.childNode(withName: "Spin", |
| recursively: true)!, radius: String(box.radius)) |
| } |
| } |
| animate(node: scene.rootNode) |
| } |
| } |
| func makeAScene() -> SCNScene { |
| let precessionNode = SCNNode() |
| let SpinNode = SCNNode() |
| precessionNode.name = "precession" |
| precessionNode.simdPosition = SIMD3<Float>(0,0,0) |
| precessionNode.simdTransform = |
| float4x4([Float(0.5.squareRoot()),Float(0.5.squareRoot()),0,0], |
| [-Float(0.5.squareRoot()),Float(0.5.squareRoot()),0,0],[0,0,1,0],[0,0,0,0]) |
| SpinNode.name = "Spin" |
| SpinNode.simdPosition = SIMD3<Float>(0, 0, 0) |
| let newScene = SCNScene() |
| newScene.rootNode.addChildNode(precessionNode) |
| newScene.rootNode.childNode(withName: "precession", recursively: |
| true)?.addChildNode(SpinNode) |
| |
| newScene.background.contents = NSColor.black |
| newScene.rootNode.camera = SCNCamera() |
| newScene.rootNode.camera?.usesOrthographicProjection = true |
| Determines whether the receiver uses an orthographic projection or not. |
| Defaults to NO. INVISIBLE WITHOUT IT! |
| newScene.rootNode.camera?.orthographicScale = 20 |
| the number the smaller the image Without specifying 20 view is too close to |
| nucleus Defaults to 1. |
| newScene.rootNode.camera?.zNear = -20 |
| value is 1.0 |
| newScene.rootNode.camera?.zFar = 100 |
| default value is 100.0 |
| |
| to 60 degrees |
| |
| newScene.rootNode.camera?.automaticallyAdjustsZRange = false |
| |
| |
| return newScene |
| } |
| extension simd_float3 { |
| func pNode(drawIntoNode: SCNNode, radius: String) { |
| let myBox = SCNBox(width: 0.5, height: 0.5, length: 0.5, chamferRadius: 0.1) |
| let newNode = SCNNode(geometry: myBox) |
| newNode.name = "box_\(radius)" |
| newNode.simdPosition = self |
| newNode.scale = SCNVector3(x: 1.0, y: 1.0, z: 1.0) |
| newNode.isHidden = false |
| newNode.opacity = 1.0 |
| transparency, while a value of 1 means 100% opacity." |
| assignMaterials(newNode) |
| drawIntoNode.addChildNode(newNode) |
| } |
| } |
| func animate(node: SCNNode) { |
| let precession_action = SCNAction.rotate(by: CGFloat(Double.pi*2), around: |
| SCNVector3Make(0, 1, 0), duration: 79.17) |
| let Spin_action = SCNAction.rotate(by: CGFloat(Double.pi*2), around: |
| SCNVector3Make(0, 1, 0), duration: 4) |
| let boxSpin_action = SCNAction.rotate(by: -CGFloat(Double.pi*2), around: |
| SCNVector3Make(0, 1, 0), duration: 0.5) |
| var childNodeList:[SCNNode] = node.childNodes(passingTest: { (anyNode, stop) |
| -> Bool in anyNode.name == "precession" } ) |
| for kids in childNodeList { |
| if !kids.hasActions { |
| kids.runAction(SCNAction.repeatForever(precession_action)) } |
| } |
| childNodeList = node.childNodes(passingTest: { (anyNode, stop) -> Bool in |
| anyNode.name == "Spin" } ) |
| for kids in childNodeList { |
| if !kids.hasActions { kids.runAction(SCNAction.repeatForever(Spin_action)) } |
| } |
| childNodeList = node.childNodes(passingTest: { (anyNode, stop) -> Bool in |
| anyNode.name!.hasPrefix("box") } ) |
| for kids in childNodeList { |
| if !kids.hasActions { |
| kids.runAction(SCNAction.repeatForever(boxSpin_action)) } |
| } |
| } |
| func assignMaterials( |
| node: SCNNode) { |
| _ |
| for (index,_) in zip((node.geometry?.elements.indices)!, |
| node.geometry!.elements) { // see enumerated() for zero based e.g., |
| Array() and ContiguousArray() only else zip(_:_:) |
| node.geometry?.materials[index].diffuse.contents = NSColor.red } |
| } |
| |