首页 > 解决方案 > pyopengl中用于旋转的pyquaternion

问题描述

我正在尝试使用 pyopgengl 创建一个简单的人类外骨骼模拟,并使用 pyquaternion 来处理旋转。线(l1 和 l2)连接形成接头。一条垂直的法线 (l3) 由关节处的有趣线创建。l2 应该能够围绕 l1 和 l3 旋转。

在代码中,箭头键围绕原点旋转相机*。按住 c 或 v 并使用键盘 + 或 - 来增加或减少引导线围绕其基线或 perp 线的旋转。按 Enter 在中间线或最远线之间循环。

有 5 行(2 行是 perp 行)。围绕其基础和 perp 轴旋转中线可以正常工作,但不适用于最远的线。尝试围绕其底部(中间)和 perp 旋转最远的线并没有得到预期的结果。

我怀疑我使用四元数存在一些问题。

*参见 main 以获得更准确的键绑定

import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import math as m
import numpy as np
from pyquaternion import Quaternion

class Point:
    def __init__(self, name, x, y, z):
        self.x = x
        self.y = y
        self.z = z
        self.color = (0, 1, 0)
        self.name = name
        self.p = [self.x, self.y, self.z]

    def __str__(self):
        return '{} - x:{} y:{} z:{}'.format(self.name, self.x, self.y, self.z)

    def __add__(self, other):
        # print('other:', type(other))
        x = self.x + other.x
        y = self.y + other.y
        z = self.z + other.z
        p = Point('',x,y,z)
        return p

    def coor(self):
        return (self.x, self.y, self.z)

    def arr(self):
        return self.p

    def setpos(self, arr):  # arr is a 3 long array
        self.x = arr[0]
        self.y = arr[1]
        self.z = arr[2]

    def draw(self):
        glBegin(GL_POINTS)
        glColor3fv(self.color)
        glVertex3fv((self.x, self.y, self.z))
        glEnd()

    def rotate_z(self, a):
        x = self.x
        y = self.y
        self.x = x * m.cos(a) - y * m.sin(a)
        self.y = x * m.sin(a) + y * m.cos(a)

    def rotate_y(self, a):
        x = self.x
        z = self.z
        self.z = z * m.cos(a) - x * m.sin(a)
        self.x = z * m.sin(a) + x * m.cos(a)

    def rotate_x(self, a):
        y = self.y
        z = self.z
        self.y = y * m.cos(a) - z * m.sin(a)
        self.z = y * m.sin(a) + z * m.cos(a)

    def translate(self, x, y, z):
        self.x += x
        self.y += y
        self.z += z
        # print(self)

    def getMag(self):
        return m.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)


class Line:
    def __init__(self, name, p1, p2):  # p1 and p2 are Point instances
        self.ogdir = p2
        # tail
        self.startp = p1
        # tip
        self.dir = p2
        # direction vector
        self.radVec = self.startp + self.dir
        # perp vector
        self.perp = 0
        self.base = 0
        self.a1 = 0
        self.a2 = 0
        self.q = 0

        self.color = 1, 0, 0
        self.name = name
        self.update()

    def update(self):
        self.radVec = self.startp + self.dir
        #self.endp = Point('', self.radVec[0], self.radVec[1], self.radVec[2])

    def getRadVec(self):
        self.update()
        return self.radVec

    def __str__(self):
        return self.name

    def draw(self):
        self.update()
        glBegin(GL_LINES)
        glColor3fv(self.color)
        glVertex3fv(self.startp.coor())
        glVertex3fv(self.radVec.coor())
        glEnd()

    def rotate_x(self, a):
        self.startp.rotate_x(a)
        self.dir.rotate_x(a)
        self.update()
        print(self.radVec)

    def rotate_y(self, a):
        self.startp.rotate_y(a)
        self.dir.rotate_y(a)
        self.update()

    def rotate_z(self, a):
        self.startp.rotate_z(a)
        self.dir.rotate_z(a)
        self.update()

    def translate(self, x, y, z):
        self.startp.translate(x, y, z)
        self.dir.translate(x, y, z)
        self.update()

    def uv(self):  # makes self turn into a unit vector;
        a = self.dir.getMag()
        self.dir.x /= a
        self.dir.y /= a
        self.dir.z /= a

    def setAxis(self, p, b):
        self.perp = p
        self.base = b

    # needs fix
    def rot(self, ax, deg):  # axis is p=0 b=1
        if ax == 0:
            self.a1 += deg
        if ax == 1:
            self.a2 += deg
        self.q = Quaternion(axis=self.base.dir.arr(), degrees=m.pi * self.a2) * Quaternion(axis=self.perp.dir.arr(), degrees=m.pi * self.a1)
        self.dir.setpos(self.q.rotate(self.dir.arr()))
        self.update()

    def rotPerp(self, deg):
        self.a1 += deg
        self.q = Quaternion(axis=self.perp.dir.arr(), degrees=m.pi * self.a1)
        self.dir.setpos(self.q.rotate(self.dir.arr()))
        self.update()

    def rotBase(self, deg):
        self.a2 += deg
        self.q = Quaternion(axis=self.base.dir.arr(), degrees=m.pi * self.a2)
        self.dir.setpos(self.q.rotate(self.dir.arr()))
        self.update()


def axis():
    glBegin(GL_LINES)

    glColor3fv((1, 0, 0))
    glVertex3fv((1, 0, 0))
    glVertex3fv((0, 0, 0))

    glColor3fv((0, 1, 0))
    glVertex3fv((0, 1, 0))
    glVertex3fv((0, 0, 0))

    glColor3fv((0, 0, 1))
    glVertex3fv((0, 0, 1))
    glVertex3fv((0, 0, 0))

    glEnd()


def recPris(lv, sv, w, h, p, c):  # lv and sv are Lines, w is the width of the square base, h is the height, p is the anchor point(lv.startp=0 or lb.radvec=1), c is a rgb array
    # create unit vectors. Lines lv and ls must be perpendicular
    l = np.array(lv.dir.arr()) / np.linalg.norm(lv.dir.arr())
    s = np.array(sv.dir.arr()) / np.linalg.norm(sv.dir.arr())
    # create 3rd perp vector
    nv = np.cross(l, s)
    n = nv / np.linalg.norm(nv)

    if p == 0:
        la = lv.startp.arr()
        lb = np.add(lv.startp.arr(), np.negative(l * h))
    elif p == 1:
        la = np.add(lv.startp.arr(), l * h)
        lb = lv.getRadVec().arr()
    else:
        print("incorrect p parameter")

    points = [
        [tuple(np.add(la, w / 2 * np.add(n, -s)))],
        [tuple(np.add(la, w / 2 * np.add(-n, -s)))],
        [tuple(np.add(la, w / 2 * np.add(-n, s)))],
        [tuple(np.add(la, w / 2 * np.add(n, s)))],
        [tuple(np.add(lb, w / 2 * np.add(n, -s)))],
        [tuple(np.add(lb, w / 2 * np.add(-n, -s)))],
        [tuple(np.add(lb, w / 2 * np.add(-n, s)))],
        [tuple(np.add(lb, w / 2 * np.add(n, s)))],
    ]

    edges = [
        [0, 1],
        [0, 3],
        [0, 4],
        [2, 1],
        [2, 3],
        [2, 6],
        [5, 1],
        [5, 4],
        [5, 6],
        [7, 3],
        [7, 4],
        [7, 6]
    ]

    surfaces = [
        [0, 1, 2, 3],
        [1, 5, 6, 2],
        [2, 6, 7, 3],
        [3, 7, 4, 0],
        [0, 4, 5, 1],
        [4, 5, 6, 7]
    ]

    # draw faces
    glBegin(GL_QUADS)
    for surface in surfaces:
        glColor3fv(c)
        for vertex in surface:
            glVertex3fv(points[vertex])
    glEnd()
    # draw edges
    glBegin(GL_LINES)
    c[1] = c[1] / 2 + .3
    for edge in edges:
        for vertex in edge:
            glColor3fv((c))
            glVertex3fv(points[vertex])
    glEnd()

def main():
    pygame.init()
    display = (1000, 800)
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)

    gluPerspective(90, (display[0] / display[1]), 0.1, 50.0)
    glTranslatef(0.0, 0.0, -10)
    glRotate(10, 1, 0, 0)
    glRotate(-45, 0, 1, 0)

    p1 = Point('p1', 0, 1, 0)
    p2 = Point('p2', 2, 0, 0)
    l1 = Line('l1', p1, p2)
    l1.color = 0, 1, 1

    p3 = Point('p3', 2, 1, 1)
    l2 = Line('l2', l1.radVec, p3)
    l2.color = 1, 0, 0

    n = tuple(np.cross(p2.coor(), p3.coor()))
    p4 = Point('p4', n[0], n[1], n[2])
    l3 = Line('l3', l1.radVec, p4)
    l3.color = 0, 0, 1
    l3.uv()

    p6 = Point('p6', 2, 0, 0)
    l4 = Line('l4', l2.radVec, p6)
    l4.color = 0, 1, 0

    n1 = tuple(np.cross(l2.dir.coor(), l4.dir.coor()))
    p7 = Point('p7', n1[0], n1[1], n[2])
    l5 = Line('l5', l2.radVec, p7)
    l5.uv()

    objects = [l2, l4]

    c1 = 0

    c = 0
    cur = objects[c]
    print(type(objects[c]), objects[c], c)

    lines = [l1, l2, l3, l4, l5]

    while True:

        # update point data
        l2.startp = l1.radVec
        l3.startp = l1.radVec
        l4.startp = l2.radVec
        l5.startp = l2.radVec
        l3.dir.setpos(np.cross(l1.dir.coor(), l2.dir.coor()))
        l5.dir.setpos(np.cross(l2.dir.coor(), l4.dir.coor()))
        l3.uv()
        l5.uv()
        l2.setAxis(l3, l1)
        l4.setAxis(l5, l2)

        #q = Quaternion(axis=cur.perp.dir.arr(), degrees=cur.a1) * Quaternion(axis=cur.base.dir.arr(), degrees=cur.a2)
        #cur.dir.setpos(q.rotate(cur.dir.arr()))

        # key commmands
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

            # zoom
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 4:
                    glTranslate(0, 0, 1.0)
                if event.button == 5:
                    glTranslate(0, 0, -1.0)

            # navigation
            keys = pygame.key.get_pressed()
            if keys[pygame.K_LEFT]:
                glRotate(5, 0, 1, 0)
            if keys[pygame.K_RIGHT]:
                glRotate(-5, 0, 1, 0)
            if keys[pygame.K_UP]:
                glRotate(5, 1, 0, 0)
            if keys[pygame.K_DOWN]:
                glRotate(-5, 1, 0, 0)
            if keys[pygame.K_KP_ENTER]:  # changes line object
                c = (c + 1) % len(objects)
                cur = objects[c]
                print(cur, c)

            # move object
            if keys[pygame.K_x]:
                # print(l2.startp)
                if keys[pygame.K_KP_PLUS]:
                    cur.translate(.05, 0, 0)
                if keys[pygame.K_KP_MINUS]:
                    cur.translate(-.05, 0, 0)
                if keys[pygame.K_KP4]:
                    cur.rotate_x(m.pi / 12)
                if keys[pygame.K_KP6]:
                    cur.rotate_x(-m.pi / 12)
            if keys[pygame.K_y]:
                if keys[pygame.K_KP_PLUS]:
                    cur.translate(0, .05, 0)
                if keys[pygame.K_KP_MINUS]:
                    cur.translate(0, -.05, 0)
                if keys[pygame.K_KP4]:
                    cur.rotate_y(m.pi / 12)
                if keys[pygame.K_KP6]:
                    cur.rotate_y(-m.pi / 12)
            if keys[pygame.K_z]:
                if keys[pygame.K_KP_PLUS]:
                    cur.translate(0, 0, .05)
                if keys[pygame.K_KP_MINUS]:
                    cur.translate(0, 0, -.05)
                if keys[pygame.K_KP4]:
                    cur.rotate_z(m.pi / 12)
                if keys[pygame.K_KP6]:
                    cur.rotate_z(-m.pi / 12)
            # rotate cur about perp
            if keys[pygame.K_v]:
                if keys[pygame.K_KP_PLUS]:
                    cur.rot(0,1)
                if keys[pygame.K_KP_MINUS]:
                    cur.rot(0,-1)
            # rotate cur about base
            if keys[pygame.K_c]:
                if keys[pygame.K_KP_PLUS]:
                    cur.rot(1,1)
                if keys[pygame.K_KP_MINUS]:
                    cur.rot(1,-1)

        # glRotatef(1,1,0,0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        # Cube()
        axis()

        l1.draw()
        l2.draw()
        l3.draw()
        l4.draw()
        l5.draw()

        #recPris(l4,l5,.5,l4.dir.getMag()/2,1,[0,0,1])

        pygame.display.flip()
        pygame.time.wait(10)


main()

标签: 3dpyopengl

解决方案


推荐阅读