Вычисление оси вращения граней Сферы

Я создаю Sphere с нуля в Maya, вместо того, чтобы создавать грани, используя список вершин сфер, мне нужно сделать плоскость и повернуть ее, чтобы она соответствовала обычной грани сфер.

введите здесь описание изображения

Моя идея состояла в том, чтобы получить центральный угол между вершинами грани Сферы по горизонтали и вертикали. Это работает для оси Y, но как только я применяю поворот X, ориентация лица теряется.

На изображении я преднамеренно повернул одну из граней Сферы по оси X, чтобы проиллюстрировать, какой поворот мне нужно рассчитать. Реализация написана на Python, поэтому при необходимости у меня есть доступ ко всем векторным методам. Обратите внимание, что эта реализация сферы предназначена для другой цели, поэтому установка может показаться немного странной!

import pymel.core as pm
import pymel.core.datatypes as dt
import pymel.util as util

degrees = util.arrays.degrees
cos     = util.arrays.cos
sin     = util.arrays.sin
atan2   = util.math.atan2
acos    = util.math.acos
sqrt    = util.math.sqrt
PI      = util.arrays.pi
TWO_PI  = PI * 2

def distance(x1, y1, z1, x2, y2, z2):
    return sqrt( (x2 - x1) ** 2 + (y2 - y1) ** 2 + (z2 - z1) ** 2 )


# Sphere class
class Sphere():

# Initialise radius (float), subdivisionsAxis (int), subdivisionsHeight (int) 
def __init__(self, radius = 10, subdivisionsAxis = 8, subdivisionsHeight = 8):

    # Loop through each subdivision on y axis
    for i in range(subdivisionsHeight):

        if i == 0 or i == subdivisionsHeight - 1:

            # Store the triangle vertices's in this list
            data = self.generateSphereData(radius, subdivisionsAxis, subdivisionsHeight, i, 'triangle')

            length = len(data) / 11
            for j in range(length):
                index = j * 11
                x1 = data[index]
                y1 = data[index + 1]
                z1 = data[index + 2]
                x2 = data[index + 3]
                y2 = data[index + 4]
                z2 = data[index + 5]
                x3 = data[index + 6]
                y3 = data[index + 7]
                z3 = data[index + 8]
                # Angle y
                ay = data[index + 9]
                # Angle z
                az = data[index + 10]

                v1 = dt.FloatVector(x1, y1, z1)
                v2 = dt.FloatVector(x2, y2, z2)
                v3 = dt.FloatVector(x3, y3, z3)

                # Ignore the top and bottom triangles for now...
                # pm.polyCreateFacet( p = [ v1, v2, v3 ] )

            else:
                # Store the quads vertices's in this list
                data = self.generateSphereData(radius, subdivisionsAxis, subdivisionsHeight, i, 'quad')

                length = len(data) / 14

                for j in range(length):
                    index = j * 14
                    x1 = data[index]
                    y1 = data[index + 1]
                    z1 = data[index + 2]
                    x2 = data[index + 3]
                    y2 = data[index + 4]
                    z2 = data[index + 5]
                    x3 = data[index + 6]
                    y3 = data[index + 7]
                    z3 = data[index + 8]
                    x4 = data[index + 9]
                    y4 = data[index + 10]
                    z4 = data[index + 11]
                    # Angle y
                    ay = data[index + 12]
                    # Angle z
                    az = data[index + 13]

                    v1 = dt.FloatVector(x1, y1, z1)
                    v2 = dt.FloatVector(x2, y2, z2)
                    v3 = dt.FloatVector(x3, y3, z3)
                    v4 = dt.FloatVector(x4, y4, z4)

                    # Calculate centroid
                    cx = (x1 + x2 + x3 + x4) / 4
                    cy = (y1 + y2 + y3 + y4) / 4
                    cz = (z1 + z2 + z3 + z4) / 4

                    # Calculate the width and height

                    # Calculate dimensions for facet
                    tw = distance(x1, y1, z1, x4, y4, z4)
                    bw = distance(x2, y2, z2, x3, y3, z3)
                    w  = tw if bw < tw else bw
                    h  = distance(x2, y2, z2, x1, y1, z1)

                    # Calculate rotation of face
                    centroid = dt.FloatVector(cx, cy, cz)

                    mesh = pm.polyPlane(width=1, height=1, subdivisionsX=1, subdivisionsY=1, axis=(1, 0, 0))
                    mesh[0].setTranslation(centroid)
                    mesh[0].setRotation([0, degrees(-ay), 0])

                    pm.spaceLocator(p=v1)
                    pm.spaceLocator(p=v2)
                    pm.spaceLocator(p=v3)
                    pm.spaceLocator(p=v4)

                    # pm.polyCreateFacet( p = [ v1, v2, v3, v4 ] )


# Generate a vertex list of the spheres current subdivision height level
# Arguments: radius (float), subdivisionsAxis (int), subdivisionsHeight (int), index (int), polygonType (string) 

def generateSphereData(self, radius, subdivisionsAxis, subdivisionsHeight, index, polygonType):
    positions = []

    if polygonType == 'triangle':
        for i in range(subdivisionsAxis):

            # If were generating the top triangles we need the triangle base to 
            # Be at the previous subdivision level, so change the index to index - 1
            if index < subdivisionsHeight: 
                nextIndex = index + 1
            else:                
                nextIndex = index - 1

            if i < subdivisionsAxis - 1:
                j = i + 1
            else:
                j = 0

            # Top vertex
            r1 = radius  * sin(index * (PI / subdivisionsAxis))
            x1 = r1      * cos(i * (TWO_PI / subdivisionsAxis))
            y1 = radius  * cos(index * (PI / subdivisionsHeight))
            z1 = r1      * sin(i * (TWO_PI / subdivisionsAxis))

            # Left vertex
            r2 = radius  * sin(nextIndex * (PI / subdivisionsAxis))
            x2 = r2      * cos(i * (TWO_PI / subdivisionsAxis))
            y2 = radius  * cos(nextIndex * (PI / subdivisionsHeight))
            z2 = r2      * sin(i * (TWO_PI / subdivisionsAxis))

            # Right vertex
            x3 = r2      * cos(j * (TWO_PI / subdivisionsAxis))
            y3 = radius  * cos(nextIndex * (PI / subdivisionsHeight))
            z3 = r2      * sin(j * (TWO_PI / subdivisionsAxis))

            # Calculate angles
            ay = 0
            az = 0

            positions += [x1, y1, z1, x2, y2, z2, x3, y3, z3, ay, az]

    elif polygonType == 'quad':

        nextIndex = index + 1

        for i in range(subdivisionsAxis):

            if i < subdivisionsAxis - 1:
                j = i + 1
            else:
                j = 0

            # Bottom y
            r1 = radius * sin(index * (PI / subdivisionsAxis))
            y1 = radius * cos(index * (PI / subdivisionsHeight))

            # Top y
            r2 = radius * sin(nextIndex * (PI / subdivisionsAxis))
            y2 = radius * cos(nextIndex * (PI / subdivisionsHeight))

            # Top left vertex
            x1 = r2     * cos(i * (TWO_PI / subdivisionsAxis))
            z1 = r2     * sin(i * (TWO_PI / subdivisionsAxis))

            # Bottom left vertex
            x2 = r1     * cos(i * (TWO_PI / subdivisionsAxis))
            z2 = r1     * sin(i * (TWO_PI / subdivisionsAxis))

            # Bottom right vertex
            x3 = r1     * cos(j * (TWO_PI / subdivisionsAxis))
            z3 = r1     * sin(j * (TWO_PI / subdivisionsAxis))

            # Top right vertex
            x4 = r2     * cos(j * (TWO_PI / subdivisionsAxis))
            z4 = r2     * sin(j * (TWO_PI / subdivisionsAxis))

            # Calculate angles
            ay1 = i * (TWO_PI / subdivisionsAxis)
            ay2 = j * (TWO_PI / subdivisionsAxis)
            ay  = ay1 + ((ay2 - ay1) / 2)

            az1 = index     * (PI / subdivisionsHeight)
            az2 = nextIndex * (PI / subdivisionsHeight)
            az  = az1 + ((az2 - az1) / 2)

            positions += [x1, y2, z1, x2, y1, z2, x3, y1, z3, x4, y2, z4, ay, az]

    return positions

Sphere(20, 8, 8)

person David    schedule 17.01.2013    source источник
comment
Я подозреваю, что ответ математически прост, но я не могу разобраться в вопросе.   -  person Beta    schedule 17.01.2013
comment
Мне нужно повернуть плоскость в ориентацию соответствующей грани сферы.   -  person David    schedule 17.01.2013
comment
Хорошо, как вы представляете самолет и лицо? И есть ли конкретная точка, вокруг которой вы хотели бы повернуть плоскость?   -  person Beta    schedule 17.01.2013
comment
У меня есть список вершин для всех четырехугольников и треугольников. Я вычислил центр лица, чтобы разместить плоскость, мне просто нужно повернуть его, чтобы он имитировал соответствующее лицо.   -  person David    schedule 17.01.2013
comment
Найдите нормали плоскости и грани (взяв векторное произведение двух соседних ребер и нормализовав их). Проверьте, чтобы нормали были почти антипараллельны скалярному произведению; если это не так, то возьмите их векторное произведение, чтобы получить ось и угол вращения, если они есть, то сначала сделайте поворот на 180 градусов, а затем возьмите векторное произведение. Это достаточно ясно? Должен ли я расширить его в ответ?   -  person Beta    schedule 17.01.2013
comment
Пожалуйста, не могли бы вы написать свой ответ выше в псевдокоде, чтобы я мог попытаться реализовать.   -  person David    schedule 17.01.2013


Ответы (2)


Хорошо, псевдокод. Как насчет этого:

planeNormal = cross(plane.firstEdge, plane.secondEdge)
faceNormal = cross(face.firstEdge, face.secondEdge)

normalize(planeNormal)
normalize(faceNormal)

if dot(planeNormal, faceNormal)<0    # if they're more than 90 degrees apart
  rotate(plane, plane.firstEdge, pi) # rotate the plane 180 degrees
  planeNormal = -planeNormal

axis = cross(planeNormal, faceNormal)
angle = arccos(magnitude(axis))
normalize(axis)
rotate(plane, axis, angle)
person Beta    schedule 17.01.2013
comment
У меня нет лица, чтобы вычислить нормаль. Плоскость, которую я создаю, является гранью сферы, поэтому любые вычисления нужно будет делать из вектора положения, который у меня есть для каждой грани сферы. - person David; 17.01.2013
comment
@David: У вас есть вектор положения для лица и (я полагаю) для центра сферы. Вычисление нормали лица из них не должно быть слишком сложным. - person Beta; 17.01.2013

Если я правильно прочитал ваш вопрос, вы хотите применить вращение к группе четырехугольников и треугольников, чтобы они образовали поверхность сферы. Я полагаю, что вы действительно хотите спросить, как повернуть набор четырехугольников и треугольников так, чтобы вектор, нормальный к центру фигуры, пересекал желаемую точку. Эта желаемая точка является центром вашей сферы.

Давайте разорвем это! Вот псевдокод того, что вы хотите сделать:

for i,shape in enumerate(listOfShapes):
    [nc1,nc2] = normal_of_center(shape)
    R = rotationFrom2Vecs([nc1,nc2],[s,nc2])
    listOfShapes[i] = shape*R

Для каждой формы (форма — это просто список из 3 или 4 точек) вы вычисляете вектор, который является нормальным к форме и центрирован в центре этой фигуры. Этот вектор состоит всего из двух точек: [nc1,nc2] (подсказка nc2: это просто средние координаты рассматриваемой формы, nc1 — это любая точка, которая лежит на нормали к плоскости, определяемой фигурой).

Следующее, что мы хотим сделать, это вычислить матрицу вращения R между этим вектором и вектором, соединяющим центр вашей фигуры (nc2) с центром сферы (s). Но как, черт возьми, мне рассчитать Матрица вращения 3x3 из 2-х векторов, наверное, вы спрашиваете себя?

Наконец, вы применяете эту матрицу поворота к фигуре с помощью умножения вправо, что, как известно, является единственным допустимым способом сделать это из-за правил умножения матриц. Я не знаком с pymel, я не знаю, есть ли у него процедуры умножения матриц или нет. Если это не так, вы можете использовать массивы и матрицы numpy, которые очень просты и удобны для проектов. нравится.

Если вас смущает общая идея, я могу нарисовать несколько быстрых и грубых диаграмм. Это самый быстрый способ? Нет. Однако это определенно самый четкий метод.

person Adam Cadien    schedule 17.01.2013
comment
Это именно то, что я ищу. Диаграммы очень помогут, если у вас есть время. - person David; 17.01.2013