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


#1

Помогите найти причину странных результатов для кода ниже. Уже всю голову сломал чтобы понять в чем причина. Вкратце. Написал Extension для SCNNode для рисования цилиндра между двумя точками в трехмерном пространстве. Использовал для этого следующий пост на StackOverflow - http://stackoverflow.com/questions/30827401/cylinder-orientation-between-two-points-on-a-sphere-scenekit-quaternions-ios

extension SCNNode
{
func makeCylinder(positionStart: SCNVector3, positionEnd: SCNVector3, radius: CGFloat , color: NSColor, transparency: CGFloat) -> SCNNode
{
    let height = CGFloat(GLKVector3Distance(SCNVector3ToGLKVector3(positionStart), SCNVector3ToGLKVector3(positionEnd)))
    let startNode = SCNNode()
    let endNode = SCNNode()
    
    startNode.position = positionStart
    endNode.position = positionEnd
    
    // additional node to align cylinder along the z axis
    let zAxisNode = SCNNode()
    zAxisNode.eulerAngles.x = CGFloat(M_PI_2)
    
    let cylinderGeometry = SCNCylinder(radius: radius, height: height)
    cylinderGeometry.firstMaterial?.diffuse.contents = color
    let cylinder = SCNNode(geometry: cylinderGeometry)
    
    
    //correction of relative positions of the cylinder start/end        
    if (positionStart.x > 0.0 && positionStart.y < 0.0 && positionStart.z < 0.0 && positionEnd.x > 0.0 && positionEnd.y < 0.0 && positionEnd.z > 0.0)
    {
        cylinder.position.y = height/2
    }
    else if (positionStart.x < 0.0 && positionStart.y < 0.0 && positionStart.z < 0.0 && positionEnd.x < 0.0 && positionEnd.y < 0.0 && positionEnd.z > 0.0)
    {
        cylinder.position.y = height/2
    }
   else  if (positionStart.x < 0.0 && positionStart.y > 0.0 && positionStart.z < 0.0 && positionEnd.x < 0.0 && positionEnd.y > 0.0 && positionEnd.z > 0.0)
    {
        cylinder.position.y = height/2
    }
   else  if (positionStart.x > 0.0 && positionStart.y > 0.0 && positionStart.z < 0.0 && positionEnd.x > 0.0 && positionEnd.y > 0.0 && positionEnd.z > 0.0)
    {
        cylinder.position.y = height/2
    }
    else 
    {
        cylinder.position.y = -height/2
    }
    
    
    zAxisNode.addChildNode(cylinder)
    
    //additional node is at startNode
    startNode.addChildNode(zAxisNode)
    //constrain the startNode by endNode
    startNode.constraints = [ SCNLookAtConstraint(target: endNode) ]

    return startNode
}

}

Ориентация цилиндра в противоположных направления определяется координатами начала и конца цилиндра и в зависимости от этого height/2 имеет знак плюс или минус в строчке

cylinder.position.y = height/2

Соответственно для этого написал блок кода

 if (positionStart.x > 0.0 && positionStart.y < 0.0 && positionStart.z < 0.0 && positionEnd.x > 0.0 && positionEnd.y < 0.0 && positionEnd.z > 0.0)
    {
        cylinder.position.y = height/2
    }
    else if (positionStart.x < 0.0 && positionStart.y < 0.0 && positionStart.z < 0.0 && positionEnd.x < 0.0 && positionEnd.y < 0.0 && positionEnd.z > 0.0)
    {
        cylinder.position.y = height/2
    }
    else if (positionStart.x < 0.0 && positionStart.y > 0.0 && positionStart.z < 0.0 && positionEnd.x < 0.0 && positionEnd.y > 0.0 && positionEnd.z > 0.0)
    {
        cylinder.position.y = height/2
    }
   else  if (positionStart.x > 0.0 && positionStart.y > 0.0 && positionStart.z < 0.0 && positionEnd.x > 0.0 && positionEnd.y > 0.0 && positionEnd.z > 0.0)
    {
        cylinder.position.y = height/2
    }
    else 
    {
        cylinder.position.y = -height/2
    }

Суть проблемы состоит в том что этот блок в разных вариантах записи работает в случае тестовых координат, когда есть куб и мы соединяем все возможные вершины. И никаких других вариантов в принципе не существует. Но когда я использую эту Extension в моей программе то иногда для одного из условий цилиндр направлен в противоположную сторону, не в ту которую он должен быть направлен. Весь маразм ситуации дополняется тем что если я переставлю местами условия то тогда для другого варианта условий возникает эта ситуация с противоположной ориентацией. Повторю, когда проверял код на тестовых координатах начала и конца цилиндра, тот же код работает как часы. В программе нет ничего другого как набор аналогичных координат как в тесте. В общем ситуация бредовая, но причина то должна быть. Помогите пожалуйста разобраться !!!


#2

Поменял немного логику в коде. Вместо того чтобы менять знак у height/2 оставил знак тот же, но для указанных условий поменял начало и конец цилиндра если изменяется знак у height/2. По сути это тоже самое что и раньше, но видимо у SCNNode() реализации есть своя внутренняя логика. В итоге неожиданности с изменением направления закончились.