iPhone等高線?

等高線グラフっぽいのを表示するiPhoneアプリのサンプルコードを描いてみます。

import UIKit

import SceneKit

import SpriteKit

class ViewController: UIViewController {

    weak var sceneView : SCNView?

    var values = Array<Int>()

    

    struct Float3 {

        var x, y, z: GLfloat

    }

    struct Float2 {

        var s, t: GLfloat

    }

    

    struct Vertex {

        var position: Float3

        var normal: Float3

        var tcoord: Float2

    }

    

    var vertices: [Float3] = {

        var arr = [Float3]()

        for i in 0..<40 {

            let col = Float(i % 8)

            let row = Float(i / 8)

            arr.append(Float3(x: col * 5, y: 0, z: row * 5))

        }

        return arr

    }()

    

    var normals: [Float3] = {

        var arr = [Float3]()

        for i in 0..<40 {

            arr.append(Float3(x: 0, y: 0.5, z: 0.5))

        }

        return arr

    }()

    

    var indices: [GLint] = {

        var arr = [GLint]()

        for i in 0..<40 {

            if (i % 8) == 7 { continue }

            if (i / 8) == 4 { continue }

            

            let n = GLint(i)

            arr.append(n + 1)

            arr.append(n)

            arr.append(n + 9)

            

            arr.append(n + 8)

            arr.append(n + 9)

            arr.append(n)

        }

        return arr

    }()

    

    

    

    override func viewDidLoad() {

        super.viewDidLoad()

        setupScene()

        updateSurfaceChart()

        createCamera()

        createLight()

    }

    func setupScene() {

        let w = CGRectGetMaxX(self.view.bounds)

        let h = CGRectGetMaxY(self.view.bounds)

        let sv = SCNView(frame: CGRectMake(0, 0, w, h))

        sv.scene = SCNScene()

        sv.backgroundColor = UIColor(hue: 0.3, saturation: 0.05, brightness: 1, alpha: 1)

        sv.allowsCameraControl = true

        self.view.addSubview(sv)

        self.sceneView = sv

        

        sv.overlaySKScene = SKScene(size: CGSize(width: w, height: h))

        let n = SKSpriteNode(color: UIColor.darkGrayColor(), size: CGSize(width: w, height: h * 0.5))

        n.position = CGPoint(x: w * 0.5, y: h * 0.25)

        sv.overlaySKScene.addChild(n)

        

        let d = CGRectGetMaxX(self.view.bounds) / 8.0

        for i in 0..<40 {

            let x = CGFloat(i % 8) * d + d * 0.5

            let y = CGFloat(i / 8) * d + 80

            let n = SKSpriteNode(color: UIColor(hue: 0, saturation: 0.3, brightness: 1, alpha: 1), size: CGSize(width: 30, height: 30))

            n.position = CGPoint(x: x, y: y)

            n.name = “marker\(i)

            n.zRotation = CGFloat(M_PI)/4.0

            sv.overlaySKScene.addChild(n)

            

            let label = SKLabelNode(text: \(vertices[i].y))

            label.fontName = UIFont.boldSystemFontOfSize(0).fontName

            label.fontSize = 15

            label.name = “label”

            n.addChild(label)

            values.append(i)

        }

    }

    

    func updateSurfaceChart() {

        self.sceneView?.scene?.rootNode.childNodeWithName(“surface”, recursively: false)?.removeFromParentNode()

        

        let vdata = NSData(bytes: vertices, length: vertices.count * sizeof(Float3))

        let vertexSource = SCNGeometrySource(data: vdata, semantic: SCNGeometrySourceSemanticVertex, vectorCount: vertices.count, floatComponents: true, componentsPerVector: 3, bytesPerComponent: sizeof(GLfloat), dataOffset: 0, dataStride: sizeof(Float3))

        

        let ndata = NSData(bytes: normals, length: normals.count * sizeof(Float3))

        let normalSource = SCNGeometrySource(data: ndata, semantic: SCNGeometrySourceSemanticNormal, vectorCount: normals.count, floatComponents: true, componentsPerVector: 3, bytesPerComponent: sizeof(GLfloat), dataOffset: 0, dataStride: sizeof(Float3))

        

        

        let indexData = NSData(bytes:indices, length:indices.count * sizeof(GLint))

        let element = SCNGeometryElement(data: indexData, primitiveType: .Triangles, primitiveCount: indices.count, bytesPerIndex: sizeof(GLint))

        

        let surface = SCNGeometry(sources: [vertexSource, normalSource], elements: [element])

        surface.firstMaterial?.diffuse.contents = UIColor.redColor()

        let sn = SCNNode(geometry: surface)

        sn.name = “surface”

        self.sceneView?.scene?.rootNode.addChildNode(sn)

    }

    

    func createCamera() {

        let camera = SCNNode()

        camera.camera = SCNCamera()

        camera.position = SCNVector3(x: 15, y: 20, z: 50)

        camera.rotation = SCNVector4(x: 1, y: 0, z: 0, w: –0.6)

        self.sceneView?.scene?.rootNode.addChildNode(camera)

    }

    

    func createLight() {

        let light = SCNLight()

        light.type = SCNLightTypeSpot

        let lightNode = SCNNode()

        lightNode.light = light

        lightNode.position = SCNVector3(x: 15, y: 5, z: 90)

        lightNode.rotation = SCNVector4(x: 1, y: 0, z: 0, w: –0.3)

        self.sceneView?.scene?.rootNode.addChildNode(lightNode)

    }

    

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {

        if let p = touches.anyObject()?.locationInNode(self.sceneView?.overlaySKScene) {

            let hit = self.sceneView!.overlaySKScene.nodeAtPoint(p)

            if (hit.name?.hasPrefix(“marker”) != nil) {

                let idx = (hit.name! as NSString).substringFromIndex(6).toInt()!

                vertices[idx].y += 2.0

                updateSurfaceChart()

                

                let label = hit.childNodeWithName(“label”) as SKLabelNode

                label.text = \((label.text as NSString).floatValue + 2.0)

            }

        }

        

    }

}