Python 计算球面的旋转轴

Python 计算球面的旋转轴,python,vector,trigonometry,maya,pymel,Python,Vector,Trigonometry,Maya,Pymel,我在Maya中从头开始构建一个球体,而不是使用球体顶点列表创建面,我需要创建一个平面并旋转它,使其与常规球体面匹配 我的想法是获得球体的面顶点水平和垂直方向之间的中心角。这适用于Y轴,但只要我应用X旋转,面方向就会丢失 在这幅图中,我仔细地旋转了X轴上的一个球面,以说明我需要计算什么样的旋转。该实现是用Python编写的,因此如果需要,我可以访问所有向量方法。请注意,此sphere实现是出于另一个目的,因此设置可能看起来有点奇怪 import pymel.core as pm import p

我在Maya中从头开始构建一个
球体
,而不是使用球体顶点列表创建面,我需要创建一个平面并旋转它,使其与常规球体面匹配

我的想法是获得球体的面顶点水平和垂直方向之间的中心角。这适用于
Y
轴,但只要我应用
X
旋转,面方向就会丢失

在这幅图中,我仔细地旋转了
X轴上的一个球面,以说明我需要计算什么样的旋转。该实现是用Python编写的,因此如果需要,我可以访问所有向量方法。请注意,此sphere实现是出于另一个目的,因此设置可能看起来有点奇怪

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)
将pymel.core导入为pm
将pymel.core.datatypes导入为dt
将pymel.util作为util导入
度=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
两个π=π*2
def距离(x1、y1、z1、x2、y2、z2):
返回sqrt((x2-x1)**2+(y2-y1)**2+(z2-z1)**2)
#球面类
类Sphere():
#初始化半径(浮点)、细分Saxis(int)、细分Sheight(int)
def uuu init uuuu(自,半径=10,细分Saxis=8,细分高度=8):
#循环通过y轴上的每个细分
对于范围内的i(细分高度):
如果i==0或i==subsectionSheight-1:
#将三角形顶点存储在此列表中
data=self.GenerateSpherateData(半径、细分Saxis、细分Sheight、i,‘三角形’)
长度=长度(数据)/11
对于范围内的j(长度):
指数=j*11
x1=数据[索引]
y1=数据[索引+1]
z1=数据[索引+2]
x2=数据[指数+3]
y2=数据[索引+4]
z2=数据[索引+5]
x3=数据[索引+6]
y3=数据[索引+7]
z3=数据[索引+8]
#角y
ay=数据[索引+9]
#角z
az=数据[指数+10]
v1=dt.FloatVector(x1,y1,z1)
v2=dt.FloatVector(x2,y2,z2)
v3=dt.FloatVector(x3,y3,z3)
#暂时忽略顶部和底部三角形。。。
#pm.polyCreateFacet(p=[v1,v2,v3])
其他:
#将四边形顶点存储在此列表中
data=self.generateSpederata(半径、细分Saxis、细分Sheight、i,‘四元’)
长度=长度(数据)/14
对于范围内的j(长度):
指数=j*14
x1=数据[索引]
y1=数据[索引+1]
z1=数据[索引+2]
x2=数据[指数+3]
y2=数据[索引+4]
z2=数据[索引+5]
x3=数据[索引+6]
y3=数据[索引+7]
z3=数据[索引+8]
x4=数据[索引+9]
y4=数据[指数+10]
z4=数据[索引+11]
#角y
ay=数据[索引+12]
#角z
az=数据[指数+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)
#计算质心
cx=(x1+x2+x3+x4)/4
cy=(y1+y2+y3+y4)/4
cz=(z1+z2+z3+z4)/4
#计算宽度和高度
#计算小平面的尺寸
tw=距离(x1,y1,z1,x4,y4,z4)
bw=距离(x2,y2,z2,x3,y3,z3)
如果bwplaneNormal = 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)
for i,shape in enumerate(listOfShapes):
    [nc1,nc2] = normal_of_center(shape)
    R = rotationFrom2Vecs([nc1,nc2],[s,nc2])
    listOfShapes[i] = shape*R