import SwiftUI
import SceneKit
// MARK: - Custom Colors
extension UIColor {
static let placeholderBackground = UIColor(red: 0.2, green: 0.5, blue: 0.8, alpha: 1.0)
static let placeholderDisabledBackground = UIColor.gray
static let placeholderFilteredOutBackground = UIColor.lightGray
static let placeholderIcon = UIColor.white
static let placeholderDisabledIcon = UIColor.white
}
// MARK: - Main View
struct ContentView: View {
@State private var gridSize: Double = 5
@State private var scene: SCNScene?
private var optimizedGridManager: OptimizedGridManager = OptimizedGridManager()
var body: some View {
ZStack {
SceneView(
scene: scene ?? createScene(),
pointOfView: createCamera(),
options: [.allowsCameraControl, .autoenablesDefaultLighting]
)
.edgesIgnoringSafeArea(.all)
.onChange(of: gridSize) { _, _ in
// Debounce scene recreation
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
scene = createScene()
}
}
VStack {
Spacer()
// Grid size slider
VStack {
Text("Grid Size: \(Int(gridSize)) x \(Int(gridSize))")
.foregroundColor(.white)
.padding(.bottom, 5)
Slider(value: $gridSize, in: 1...150, step: 1)
.padding(.horizontal)
}
.padding()
.background(Color.black.opacity(0.7))
.cornerRadius(10)
.padding()
}
}
}
private func createScene() -> SCNScene {
let scene = SCNScene()
// Add ambient light
let ambientLight = SCNNode()
ambientLight.light = SCNLight()
ambientLight.light?.type = .ambient
ambientLight.light?.intensity = 100
scene.rootNode.addChildNode(ambientLight)
// Add directional light
let directionalLight = SCNNode()
directionalLight.light = SCNLight()
directionalLight.light?.type = .directional
directionalLight.light?.intensity = 1000
directionalLight.position = SCNVector3(x: 0, y: 10, z: 10)
directionalLight.eulerAngles = SCNVector3(x: -Float.pi/4, y: 0, z: 0)
scene.rootNode.addChildNode(directionalLight)
// Create grid of nodes
optimizedGridManager.createGrid(size: Int(gridSize), in: scene)
return scene
}
private func createCamera() -> SCNNode {
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 5, z: 10)
cameraNode.eulerAngles = SCNVector3(x: -Float.pi/6, y: 0, z: 0)
return cameraNode
}
}
class OptimizedGridManager {
private let geometryPool: GeometryPool
private var gridNode: SCNNode?
init() {
// Create shared geometry instances
self.geometryPool = GeometryPool()
}
private class GeometryPool {
let cylinderGeometry: SCNCylinder
let planeGeometry: SCNPlane
init() {
// Create shared geometries
cylinderGeometry = SCNCylinder(radius: 0.125, height: 0.02)
cylinderGeometry.materials.first?.diffuse.contents = UIColor.placeholderBackground
planeGeometry = SCNPlane(width: 0.18, height: 0.18)
planeGeometry.firstMaterial?.diffuse.contents = UIImage(systemName: "star.fill")
planeGeometry.firstMaterial?.lightingModel = .constant
}
}
func createGrid(size: Int, in scene: SCNScene) {
// Remove existing grid
gridNode?.removeFromParentNode()
// Create a single parent node for the entire grid
let newGridNode = SCNNode()
// Create geometry instances once
let cylinderGeometry = geometryPool.cylinderGeometry
let planeGeometry = geometryPool.planeGeometry
// Use SCNTransaction to batch updates
SCNTransaction.begin()
SCNTransaction.animationDuration = 0
let spacing = 1.0
let offset = Double(size-1) * spacing/2
// Pre-calculate transforms for better performance
var transforms: [SCNMatrix4] = []
transforms.reserveCapacity(size * size)
for row in 0..