Как мне сделать шестиугольник с 6 три angular узлами SCN? - PullRequest
3 голосов
/ 02 марта 2020

Я пытаюсь сделать шестиугольную сетку с треугольниками, не меняя точки поворота, но я не могу правильно расположить треугольники, чтобы сделать один шестиугольник. Я создаю SCNNodes с UIBezierPaths, чтобы сформировать треугольники, а затем вращаю пути Безье. Кажется, это работает нормально, пока я не попробую использовать уравнение параметрирования c, чтобы расположить треугольники вокруг круга, чтобы сформировать шестиугольник, тогда они не окажутся в правильном положении. Можете ли вы помочь мне определить, где я делаю неправильно?

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)
    }
}

Как это выглядит в настоящее время:

enter image description here

Как это должно выглядеть:

enter image description here

Ответы [ 2 ]

4 голосов
/ 06 марта 2020

Самый простой способ получить шестиугольник - это использовать 6 масштабированных SCNPyramids со смещенными точками поворота :

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 pyramid is almost zero
            triangleNode.scale = SCNVector3(5, 5, 0.001)

            // move a pivot point from pyramid base to its 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

2 голосов
/ 30 апреля 2020

Есть несколько проблем с кодом в его нынешнем виде. Во-первых, как указано в комментариях, уравнение параметри c для переводов необходимо повернуть на 90 градусов:

        let deg: Float = (Float(i) * 60) - 90.0

Следующая проблема заключается в том, что центр ограничительной рамки треугольника и Центр тяжести треугольника не совпадают. Это важно, потому что уравнение параметри c вычисляет, где должны располагаться центроиды треугольников, а не центры их ограничивающих прямоугольников. Итак, нам нужен способ расчета центроида. Это можно сделать, добавив следующий метод расширения к TrianglePlane:

extension TrianglePlane {
    /// Calculates the centroid of the triangle
    func centroid() -> CGPoint
    {
        let max: CGFloat = self.size
        let min: CGFloat = 0
        let peak = UIBezierPath.middlePeak(height: max)
        let xAvg = (min + max / CGFloat(2.0) + max) / CGFloat(3.0)
        let yAvg = (min + peak + min) / CGFloat(3.0)
        return CGPoint(x: xAvg,  y: yAvg)
    }
}

. Это позволяет вычислить правильное radius для уравнения параметрирования c:

        let height = Float(UIBezierPath.middlePeak(height: tri.size))
        let centroid = tri.centroid()
        let radius = height - Float(centroid.y)

Окончательная поправка заключается в расчете смещения между началом треугольника и центроидом. Это исправление зависит от того, был ли треугольник перевернут поворотом или нет:

        let x1 =  radius * cos(radians)
        let y1 =  radius * sin(radians)
        let dx = Float(-centroid.x)
        let dy = (i % 2 == 0) ? Float(centroid.y) - height  : Float(-centroid.y)
        tri.position.x = x1 + dx
        tri.position.y = y1 + dy

Соединение всего этого вместе дает желаемый результат.

enter image description here

Полный рабочий ViewController можно найти в этом gist

Примечание код можно значительно упростить, сделав начало треугольника центром тяжести.

...