ios - 如何用 6 个三角形 SCNNode 制作六边形?

标签 ios swift macos scenekit realitykit

我正在尝试在不更改任何枢轴点的情况下制作带有三角形的六边形网格,但我似乎无法正确定位三角形以制作单个六边形。我正在使用 UIBezierPaths 创建 SCNNodes 以形成三角形,然后旋转贝塞尔路径。这似乎工作正常,直到我尝试使用参数方程将三角形定位在一个圆圈周围以形成六边形,然后它们最终没有处于正确的位置。你能帮我找出我在哪里做错了吗?

class TrianglePlane: SCNNode {

    var size: CGFloat = 0.1
    var coords: SCNVector3 = SCNVector3Zero
    var innerCoords: Int = 0

    init(coords: SCNVector3, innerCoords: Int, identifier: Int) {
        super.init()

        self.coords = coords
        self.innerCoords = innerCoords
        setup()
    }

    init(identifier: Int) {
        super.init()
//        super.init(identifier: identifier)
        setup()
    }

    required init?(coder aDecoder: NSCoder) { 
        fatalError("init(coder:) has not been implemented") 
    }

    func setup() {
        let myPath = path()
        let geo = SCNShape(path: myPath, extrusionDepth: 0)
        geo.firstMaterial?.diffuse.contents = UIColor.red
        geo.firstMaterial?.blendMode = .multiply
        self.geometry = geo
    }

    func path() -> UIBezierPath {

        let max: CGFloat = self.size
        let min: CGFloat = 0

        let bPath = UIBezierPath()
        bPath.move(to: .zero)
        bPath.addLine(to: CGPoint(x: max / 2, 
                                  y: UIBezierPath.middlePeak(height: max)))
        bPath.addLine(to: CGPoint(x: max, y: min))
        bPath.close()
        return bPath
    }
}

extension TrianglePlane {

    static func generateHexagon() -> [TrianglePlane] {

        var myArr: [TrianglePlane] = []

        let colors = [UIColor.red, UIColor.green, 
                      UIColor.yellow, UIColor.systemTeal, 
                      UIColor.cyan, UIColor.magenta]


        for i in 0 ..< 6  {

            let tri = TrianglePlane(identifier: 0)
            tri.geometry?.firstMaterial?.diffuse.contents = colors[i]
            tri.position = SCNVector3( -0.05, 0, -0.5)

//          Rotate bezier path
            let angleInDegrees = (Float(i) + 1) * 180.0
            print(angleInDegrees)
            let angle = CGFloat(deg2rad(angleInDegrees))
            let geo = tri.geometry as! SCNShape
            let path = geo.path!
            path.rotateAroundCenter(angle: angle)
            geo.path = path

//          Position triangle in hexagon
            let radius = Float(tri.size)/2
            let deg: Float = Float(i) * 60
            let radians = deg2rad(-deg)

            let x1 = tri.position.x + radius * cos(radians)
            let y1 = tri.position.y + radius * sin(radians)
            tri.position.x = x1
            tri.position.y = y1

            myArr.append(tri)
        }

        return myArr
    }

    static func deg2rad(_ number: Float) -> Float {
        return number * Float.pi / 180
    }
}

extension UIBezierPath {

    func rotateAroundCenter(angle: CGFloat) {

        let center = self.bounds.center
        var transform = CGAffineTransform.identity
        transform = transform.translatedBy(x: center.x, y: center.y)
        transform = transform.rotated(by: angle)
        transform = transform.translatedBy(x: -center.x, y: -center.y)
        self.apply(transform)
    }

    static func middlePeak(height: CGFloat) -> CGFloat {
        return sqrt(3.0) / 2 * height
    }
}

extension CGRect {
    var center : CGPoint {
        return CGPoint(x:self.midX, y:self.midY)
    }
}

What it currently looks like:

enter image description here

What it SHOULD look like:

enter image description here

最佳答案

我创建了两个版本 - SceneKit 和 RealityKit。


SceneKit(macOS 版本)

组成六边形的最简单方法是使用六个非均匀缩放的 SCNPyramids(平面)及其偏移的枢轴点。每个“三角形”必须以 60 度增量旋转 (.pi/3)。

import SceneKit

class ViewController: NSViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let sceneView = self.view as! SCNView
        let scene = SCNScene()
        sceneView.scene = scene
        sceneView.allowsCameraControl = true
        sceneView.backgroundColor = NSColor.white

        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        scene.rootNode.addChildNode(cameraNode)
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
        
        for i in 1...6 {

            let triangleNode = SCNNode(geometry: SCNPyramid(width: 1.15,
                                                           height: 1,
                                                           length: 1))

            // the depth of the pyramid is almost zero
            triangleNode.scale = SCNVector3(5, 5, 0.001)

            // move a pivot point from pyramid its base to upper vertex
            triangleNode.simdPivot.columns.3.y = 1     

            triangleNode.geometry?.firstMaterial?.diffuse.contents = NSColor(
                                                 calibratedHue: CGFloat(i)/6,
                                                    saturation: 1.0, 
                                                    brightness: 1.0, 
                                                         alpha: 1.0)

            triangleNode.rotation = SCNVector4(0, 0, 1, 
                                              -CGFloat.pi/3 * CGFloat(i))

            scene.rootNode.addChildNode(triangleNode)
        }
    }
}

enter image description here


RealityKit(iOS 版)

在这个项目中,我在 MeshDescriptor 的帮助下生成了一个三角形,并将其复制了 5 次。

import UIKit
import RealityKit

class ViewController: UIViewController {

    @IBOutlet var arView: ARView!
    let anchor = AnchorEntity()
    let camera = PointOfView()
    let indices: [UInt32] = [0, 1, 2]

    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.arView.environment.background = .color(.black)
        self.arView.cameraMode = .nonAR
        self.camera.position.z = 9

        let positions: [simd_float3] = [[ 0.00, 0.00, 0.00],
                                        [ 0.52, 0.90, 0.00],
                                        [-0.52, 0.90, 0.00]]
         
        var descriptor = MeshDescriptor(name: "Hexagon's side")
        descriptor.materials = .perFace(self.indices)
        descriptor.primitives = .triangles(self.indices)
        descriptor.positions = MeshBuffers.Positions(positions[0...2])
        
        var material = UnlitMaterial()
        let mesh: MeshResource = try! .generate(from: [descriptor])
        
        let colors: [UIColor] = [.systemRed, .systemGreen, .yellow,
                                 .systemTeal, .cyan, .magenta]
        
        for i in 0...5 {

            material.color = .init(tint: colors[i], texture: nil)
            let triangleModel = ModelEntity(mesh: mesh, 
                                       materials: [material])

            let trianglePivot = Entity()    // made to control pivot point
            trianglePivot.addChild(triangleModel)
            
            trianglePivot.orientation = simd_quatf(angle: -.pi/3 * Float(i),
                                                    axis: [0,0,1])
            self.anchor.addChild(trianglePivot)
        }
        
        self.anchor.addChild(self.camera)
        self.arView.scene.anchors.append(self.anchor)
    }
}

enter image description here

关于ios - 如何用 6 个三角形 SCNNode 制作六边形?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60484787/

相关文章:

swift - 从 macOS 应用程序调用时使用 "key code"的 applescript 中的错误 1002

xcode - 检测互联网连接任何变化的最佳方法 xcode swift

macos - AVAudioPlayer.play() 不播放声音

ios - iPad 应用程序启动图像被截断

ios - Xcode - 存档产品转到其他项目而不是 iOS 应用程序

objective-c - 字符串中的日期不起作用

objective-c - 有没有办法(在构建时)判断我的 10.9 应用程序是否可以在 10.8 上运行?

mysql - 如何在 OS X 上以 root 身份登录 MySQL?

ios - cell.accessoryType = .Checkmark

ios - 如何从 iOS 上的 native Facebook 应用程序在 Safari 中打开移动网络应用程序链接?